Rust 基于父子关系的借用检查器

Rust 基于父子关系的借用检查器,rust,borrow-checker,Rust,Borrow Checker,下面的代码在注释中标记了生成错误消息的代码。我想我理解了这个信息:我想借用父对象两次:一次是为了找到它的子对象,另一次是作为子对象的参数(错误中的可变/不可变单词是不相关的)。我必须证明Child在修改Parent时不会消失。但我不知道怎么做。我可以Rc任何东西,但那是浪费,所以我希望增加一些生命周期就可以了 struct Parent { used: i32, child: Child, } struct Child { dummy: i32, } impl Chi

下面的代码在注释中标记了生成错误消息的代码。我想我理解了这个信息:我想借用父对象两次:一次是为了找到它的子对象,另一次是作为子对象的参数(错误中的可变/不可变单词是不相关的)。我必须证明
Child
在修改
Parent
时不会消失。但我不知道怎么做。我可以
Rc
任何东西,但那是浪费,所以我希望增加一些生命周期就可以了

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);
}
错误中的可变/不可变词不相关

我不知道你为什么这么想。易变性在锈病中非常重要!例如,虽然允许同时对不可变数据进行多个引用,但一次只允许对可变数据进行一个引用

首先,您需要修复父级的易变性:

let mut parent = // ...
然后,您将从以下行中获得一个错误:

parent.child.use_parent(&mut parent);
当您运行此行时,您隐式地可变地借用了
parent
child
。这样做是为了您可以调用
use\u parent
,这需要
和mut self

impl Parent { 
    fn use_parent(&mut self) {
        // use both child and parent
        self.used += self.child.dummy;
        self.child.dummy += 1;
    }
}
但是,您还试图获得第二个可变引用作为参数!这是否定的,因为如果允许您有多个别名可变引用,编译器将无法跟踪它并确保您不会破坏内存安全保证

假设我删除了行
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只检查被调用函数的签名,以验证借用某个函数是否安全


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

您收到错误消息的原因不同。您有一个不可变的变量
父项
,正在尝试为其创建一个
&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);
                                                 ^

针对您的评论:


不幸的是,解决方案适用于这个简化的问题,但不适用于我的实际问题。父对象有一个子对象向量,该向量可能包含较深的嵌套孙子对象。我不能只说自己是孩子

由于不应该修改向量(也不能修改,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\u parent
的参数指向同一
parent
对象的部分。也许这可以用一生的时间来完成,我不确定,但我会对此非常感兴趣。注意:
Rc
也有同样的问题。

假设我删除了self行。dummy+=1;所以只有1个可变别名-我可以让它工作吗?您只能有一个可变引用或任意数量的不可变引用。没有其他的组合是可能的基本上,正如我理解你们,我必须使用Rc+RefCell并承担一些开销,或者进入不安全的世界。现在我将尝试使用Rc+RefCell,性能在这里并不是那么重要。由于我现在正在与Rc进行斗争,在不久的将来可能会出现后续stackoverflow问题,但我将首先在自己身上尝试更多。或者第三个版本(见我的答案)是将子向量与其他数据分离。不幸的是,解决方案适用于这个简化的问题,但不适用于我的实际问题。父对象有一个子对象向量,该向量可能包含较深的嵌套孙子对象。我不能只说self.child,而不是传递一个
&mut Parent
传递一个
&mut i32
Parent.used
。参见playpen: