【问题标题】:How to store struct reference in a vec and use the struct elsewhere later on?如何将结构引用存储在 vec 中并稍后在其他地方使用该结构?
【发布时间】:2019-10-31 00:59:00
【问题描述】:

我想创建一个结构体,在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, second mutable borrow occurs here"
    // ...
    // use the sum and product vars when constructing rules
}

如何解决这个问题?

【问题讨论】:

    标签: rust


    【解决方案1】:

    你不能这样做。这只是变相的经典“矢量推送”示例。让我们看一下这个简化但语义等效的代码:

    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)很有用。它会很容易地解决你的问题。但在许多其他情况下,您最好完全重新考虑您的应用程序。

    【讨论】:

      【解决方案2】:

      在任何给定时间都可能发生一次&amp;mut 数据借用。

      我建议颠倒事情,以便Grammar 具有添加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 基于来自@Lukas Kalbertodt 的反馈。 可以通过g.non_terminals遍历所有非终端

      【讨论】:

      • 这不是一个真正的解决方案:一旦您开始使用下面的product_ref,代码将不再编译。 Playground。由于 NLL 和 product_ref 未使用,它现在可以正常工作。此解决方案不会改变任何有关潜在(故意)限制的内容。
      猜你喜欢
      • 2020-12-03
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 2012-11-25
      • 1970-01-01
      • 2021-12-21
      相关资源
      最近更新 更多