如何解决 "Returns a value referencing data owned by the current function" (结构之间的实际依赖关系)

How to solve "Returns a value referencing data owned by the current function" (Actual depenecies between structs)

我正在学习 Rust(我是一名 C++ 开发人员)并且我还在习惯借用检查器。 我有以下示例(也在 godbolt 上:https://godbolt.org/z/z873x9cPn):

struct Foo {
    value: i32,
}

struct Bar <'a> {
    foo: &'a mut Foo,
}

struct Parent <'a> {
    foo: Foo,
    bar: Bar<'a>,
}

impl <'a> Bar <'a> {
    fn new(foo: &'a mut Foo) -> Self {
        Self {
            foo
        }
    }
}

impl <'a> Parent <'a> {
    fn new() -> Self {
        let mut foo = Foo{ value: 2};
        let bar = Bar::new(&mut foo);

        Self {
            foo,
            bar,
        }
    } 
}

fn main () {
    let _parent = Parent::new();
}

但是在尝试编译时出现错误:

error[E0515]: cannot return value referencing local variable `foo`
  --> <source>:27:9
   |
25 |           let bar = Bar::new(&mut foo);
   |                              -------- `foo` is borrowed here
26 | 
27 | /         Self {
28 | |             foo,
29 | |             bar,
30 | |         }
   | |_________^ returns a value referencing data owned by the current function

我一直在浏览其他帖子,但它们并没有完全解决这种依赖关系。另外,我一直在想办法,但没有找到解决办法。

最好的解决方案是什么?

如果您在 C++ 中以与在 Rust 中相同的方式实现它,则会出现未定义的行为。 Rust 实际上是在拯救你。

&mut foo 创建对 function-local foo 的引用,但随后您 foo 移动到一个新实例中Parent,因此引用不再有效。 Rust 在编译时捕捉到了这一点。当存在对值的引用时,您不能移动或删除该值。

从实现复杂性的角度来看,最简单 的解决方法是使用 reference-counted 智能指针 (Rc),它会给出 BarParent 共享 Foo 的所有权。 (这类似于 C++ std::shared_ptr 模板,并且具有几乎相同的语义。)

但是,由于您放弃了对 Foomutable 引用,所以这很复杂。 Rust 不允许您获得对 Rc 持有的值的可变引用,除非当时 只有一个 Rc 存在。

您可以使用 CellRefCell 来解决这个问题,它们提供 interior mutabilityRefCell 更灵活但有更多的运行时开销,因为它在运行时实现了 Rust 的借用规则,这意味着如果你不正确地使用它,它也会 panic。

这就是它的样子:

use std::rc::Rc;
use std::cell::RefCell;

struct Foo {
    value: i32,
}

struct Bar {
    foo: Rc<RefCell<Foo>>,
}

struct Parent {
    foo: Rc<RefCell<Foo>>,
    bar: Bar,
}

impl Bar {
    fn new(foo: Rc<RefCell<Foo>>) -> Self {
        Self {
            foo
        }
    }
}

impl Parent {
    fn new() -> Self {
        let mut foo = Rc::new(RefCell::new(Foo { value: 2 }));
        let bar = Bar::new(foo.clone());

        Self {
            foo,
            bar,
        }
    } 
}

由于您尝试创建的结构是 self-referential,因此处理此问题的非 Rc 方法将涉及 pinningunsafe。基本上,在对它进行任何引用之前,您必须在稳定的内存位置创建 Foo,并且需要 Pin<_> 来确保值的内存位置永远不会改变。如果您希望值通过 ParentBar.

可变,您可能仍然需要 RefCell