dwjbosman
dwjbosman

Reputation: 966

How to store struct reference in a vec and use the struct elsewhere later on?

I want to create a struct, store a reference to it in a vec, and then use the struct later on:

pub struct NonTerminal {
    name: String
}

impl<'a> NonTerminal {
    pub fn new(grammar: &'a mut Grammar, name: &str) -> &'a NonTerminal {
        let t = NonTerminal {
            name: name.to_string()
        };
        grammar.non_terminals.push(t);
        grammar.non_terminals.last().unwrap()
    }
}

pub struct Grammar<'a> {
    non_terminals: Vec<NonTerminal>
}

pub fn create_rules<'a>(g: &'a mut grammar::Grammar<'a>) {
    let sum  =  grammar::NonTerminal::new(g, "Sum");
    let product = grammar::NonTerminal::new(g, "Product");
    // this fails: "cannot borrow `*g` as mutable more than once at a time, second mutable borrow occurs here"
    // ...
    // use the sum and product vars when constructing rules
}

How to approach this?

Upvotes: 0

Views: 796

Answers (2)

stacksonstacks
stacksonstacks

Reputation: 9313

Exactly one &mut borrow of data may occur at any given time.

I'd suggest inverting things so that Grammar has a function to add NonTerminals

pub struct NonTerminal {
    name: String
}

impl NonTerminal {
    pub fn new(name: &str) -> NonTerminal {
        Self {
            name: name.to_string()
        }
    }
}

pub struct Grammar {
    pub non_terminals: Vec<NonTerminal>
}

impl Grammar {
    fn add_non_terminal(&mut self, s: &str) -> &NonTerminal {
        self.non_terminals.push(NonTerminal::new(s));
        self.non_terminals.last().unwrap()
    }
}

pub fn main() {
    let mut g = Grammar {
        non_terminals: vec![]
    };
    let product_ref = g.add_non_terminal("Product");
    let sum_ref = g.add_non_terminal("Sum");
}

Updated based on feedback from @Lukas Kalbertodt. Can iterate over all non-terminals via g.non_terminals

Upvotes: 1

Lukas Kalbertodt
Lukas Kalbertodt

Reputation: 88516

You can't do this. This is simply the classical "vector push" example in disguise. Let's take a look at this simplified, but semantically equivalent code:

let mut v = Vec::new();
v.push(27);
let twenty_seven = v.last().unwrap();
v.push(3);
println!("{}", twenty_seven);

This does not compile and will never compile in Rust as this is inherently memory unsafe. You have a reference (a pointer) to an element in the vector. But the call to Vec::push might reallocate, invalidating all references to its elements. In C++ it would compile, but lead to UB because you would attempt to read uninitialized or unallocated memory.

The "answer" to your problem is... not simple. You have to think of another structure for your program. Sometimes, using reference counted smart pointers (like Rc) is useful. And it would easily solve your problem. But in many other situations you are better of completely rethinking your application.

Upvotes: 2

Related Questions