【问题标题】:Borrow checker on parent-child relation借用检查父子关系
【发布时间】:2015-03-03 13:56:13
【问题描述】:

我有下面的代码生成在其 cmets 中标记的错误消息。我想我理解了这个信息:我想借用 parent 两次:一次是为了找到它的孩子,一次是作为孩子的参数(并且错误中的可变/不可变词不相关)。我必须证明Child在修改Parent时不会消失。但我不知道该怎么做。我可以Rc<Child> 一切,但接缝很浪费,所以我希望添加一些生命周期可以解决问题。

struct Parent {
    used: i32,
    child: Child,
}

struct Child {
    dummy: i32,
}

impl Child { 
    fn use_parent(&mut self, parent: &mut Parent) {
        // use both child and parent
        parent.used += self.dummy;
        self.dummy += 1;
    }
}
fn main() {
    let parent = Parent {
        used: 0,
        child: Child {
            dummy: 1
        }
    };
    //Error: cannot borrow immutable local variable `parent` as mutable
    parent.child.use_parent(&mut parent);
}

【问题讨论】:

    标签: rust borrow-checker


    【解决方案1】:

    和错误中的可变/不可变词不相关

    我不知道你为什么这么认为。可变性在 Rust 中非常很重要!例如,虽然您可以同时对不可变数据进行多次引用,但一次只能对可变数据进行一次引用。

    首先,你需要修复parent的可变性:

    let mut parent = // ...
    

    然后,你会从该行得到一个错误:

    parent.child.use_parent(&mut parent);
    

    当您运行此行时,您隐式可变地借用 parentchild。这样做是为了让您可以调用use_parent,这需要&mut self

    但是,您试图获得第二个可变引用作为参数!这是一个禁忌,因为如果允许您拥有多个 aliasing 可变引用,编译器将无法跟踪它并确保您不会破坏内存安全保证.

    假设我删除了 self.dummy+=1; 行,所以只有 1 个可变别名 - 我可以让它工作吗?

    让我们看看函数签名的一些变体

    fn use_parent(&self, parent: &mut Parent)
    // cannot borrow `parent` as mutable because `parent.child` is also borrowed as immutable
    
    fn use_parent(&mut self, parent: &Parent)
    // cannot borrow `parent` as immutable because `parent.child` is also borrowed as mutable
    
    fn use_parent(&self, parent: &Parent)
    // OK
    

    正如我之前提到的,如果您对某事物有一个可变引用,则不允许您对同一事物有任何其他引用(可变与否)。

    另外,请注意无关紧要方法的主体是什么! Rust 只检查被调用函数的签名来验证借用东西是否安全。

    那么您如何尝试解决您的问题?最终,您正在尝试做一些编译器很难证明安全的事情。你想要一个可变链接的图表。我强烈建议阅读module documentation for Rc,其中有一个正是这种父子关系的示例。

    【讨论】:

    • 假设我删除了 self.dummy+=1;所以只有 1 个可变别名 - 我可以让它工作吗?
    • 您只能拥有一个可变引用或任意数量的不可变引用。没有其他组合是可能的
    • 基本上,据我了解,我必须使用 Rc+RefCell 并承担一些开销,否则会进入不安全的世界。我现在会尝试使用 Rc+RefCell,性能在这里并不是那么重要。由于我现在正在与 Rc 战斗,在不久的将来可能会出现后续的 stackoverflow 问题,但我会先在自己身上尝试更多。
    • 或第三个版本(见我的回答)是将孩子的向量与其他数据分开。
    【解决方案2】:

    您收到错误消息的原因不同。您有一个非可变变量 parent 并试图为其创建一个 &mut。修复你得到的

    let mut parent = Parent {
        used: 0,
        child: Child {
            dummy: 1
        }
    };
    parent.child.use_parent(&mut parent);
    

    以及相应的错误

    <anon>:31:34: 31:40 error: cannot borrow `parent` as mutable more than once at a time
    <anon>:31     parent.child.use_parent(&mut parent);
                                               ^~~~~~
    <anon>:31:5: 31:17 note: previous borrow of `parent.child` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `parent.child` until the borrow ends
    <anon>:31     parent.child.use_parent(&mut parent);
                  ^~~~~~~~~~~~
    <anon>:31:41: 31:41 note: previous borrow ends here
    <anon>:31     parent.child.use_parent(&mut parent);
                                                     ^
    

    你几乎得出了正确的结论。

    我要证明 Child 在修改 Parent 时不会消失

    不完全是。您必须证明您永远不会有两个&amp;mut 或一个&amp;mut 和一个&amp; 给孩子。如果你有一个&amp;mut 给父母,你可以用它来给孩子一个&amp;mut。因此,如果你有一个&amp;mut 给父母,一个&amp;mut 给孩子,你可以得到两个&amp;mut 给孩子。


    我看到的唯一解决方案是将use 函数移动到Parent 类型并通过self 访问child

    impl Parent { 
        fn use_parent(&mut self) {
            // use both child and parent
            self.used += self.child.dummy;
            self.child.dummy += 1;
        }
    }
    

    解决您的评论:

    不幸的是,该解决方案适用于这个简化的问题,但不适用于我的实际问题。父母有一个孩子的向量,它可能有深层嵌套的孙子。我不能只说self.child

    由于你不应该修改你的向量(而且不能,Rust 保护你),因为这会使对子节点的引用无效,你可以将这些部分传递给你需要的函数,但没有一个部分可以是孩子的直接父母。

    impl Child { 
        fn use_parent(&mut self, used: &mut i32) {
            // use both child and parent
            *used += self.dummy;
            self.dummy += 1;
        }
    }
    
    fn main() {
        let mut parent = Parent {
            used: 0,
            child: Child {
                dummy: 1
            }
        };
        // although both point to elements of the same structure
        // it is guaranteed at compile-time that they point to
        // non-overlapping parts
        let child = &mut parent.child;
        let used = &mut parent.used;
        child.use_parent(used);
    }
    

    不幸的是,我没有办法证明use_parent 的参数指向同一个Parent 对象的一部分。也许这可以用一生来完成,我不确定,但我会对此非常感兴趣。注意:Rc 也有同样的问题。

    【讨论】:

    • 不幸的是,该解决方案适用于这个简化的问题,但不适用于我的实际问题。父母有一个孩子的向量,它可能有深层嵌套的孙子。我不能只说 self.child,
    • 那么,不要将&amp;mut Parent 传递给&amp;mut i32,而是将&amp;mut i32 传递给parent.used。见游戏围栏:is.gd/SbJJik
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多