Основы: в Rust, как создать экземпляр структуры, сохранить ее как ссылку в векторе и сохранить оригинал для дальнейшего использования - PullRequest
0 голосов
/ 31 октября 2019

Я занят своими первыми шагами в Rust. Я хочу создать экземпляр структуры, сохранить ссылку на нее в Vec и позже использовать структуру:

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\n\nsecond mutable borrow occurs here",

....
    // use the sum and product vars when constructing rules
}

Как подойти к этому?

Ответы [ 2 ]

2 голосов
/ 31 октября 2019

Вы не можете сделать это. Это просто замаскированный классический пример «векторного толчка». Давайте посмотрим на этот упрощенный, но семантически эквивалентный код:

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

Это не компилируется и никогда не будет компилироваться в Rust, поскольку это по своей природе небезопасно для памяти. У вас есть ссылка (указатель) на элемент в векторе. Но вызов Vec::push может перераспределить, аннулируя все ссылки на его элементы. В C ++ он компилируется, но приводит к UB, потому что вы пытаетесь прочитать неинициализированную или нераспределенную память.

«Ответ» на вашу проблему ... не простой. Вы должны подумать о другой структуре вашей программы. Иногда полезно использовать умные указатели с подсчетом ссылок (например, Rc). И это легко решит вашу проблему. Но во многих других ситуациях вам лучше полностью переосмыслить свое приложение.

1 голос
/ 31 октября 2019

Ровно один &mut заем данных может произойти в любой момент времени.

Я бы предложил инвертировать вещи, чтобы у Grammar была функция для добавления NonTerminal s

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");
}

Обновлено на основе отзывов @Lukas Kalbertodt. Может перебирать все нетерминалы через g.non_terminals

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...