借用亲子关系的检查员

时间:2015-03-03 13:56:14

标签: rust borrow-checker

我在下面的代码中生成了注释中标记的错误消息。我想我理解这个信息:我想借父母两次:一次是为了找到它的孩子,一次是作为孩子的一个参数(并且错误中的可变/不可变的词是不相关的)。我必须证明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);
}

2 个答案:

答案 0 :(得分:1)

  

并且错误中的可变/不可变单词不相关

我不确定你为什么这么想。在Rust中,可变性非常重要非常重要例如,虽然允许同时多次引用不可变数据,但一次只允许对可变数据进行一次引用。

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

let mut parent = // ...

然后,您将从该行获得错误:

parent.child.use_parent(&mut parent);

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

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

  

假设我删除了行self.dummy+=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,其中有一个确切的亲子关系的例子。

答案 1 :(得分:1)

您收到错误消息的原因不同。您有一个不可变的变量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

时不会消失

不完全。您必须证明您的孩子永远不会有两个&mut或一个&mut和一个&。如果你的父母有&mut,你可以用它来给孩子一个&mut。因此,如果您的父母有&mut,孩子有&mut,那么您可以为孩子提供两个&mut


我看到的唯一解决方案是将use功能移至Parent类型,然后通过child访问self

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有同样的问题。