谁借了变量?

jfe*_*ard 12 lifetime rust borrow-checker

我正在与借阅检查员打架.我有两个相似的代码片段,一个像我期望的那样工作,另一个没有.

一个像我期望的那样工作:

mod case1 {
    struct Foo {}

    struct Bar1 {
        x: Foo,
    }

    impl Bar1 {
        fn f<'a>(&'a mut self) -> &'a Foo {
            &self.x
        }
    }

    // only for example
    fn f1() {
        let mut bar = Bar1 { x: Foo {} };
        let y = bar.f(); // (1) 'bar' is borrowed by 'y'
        let z = bar.f();  // error (as expected) : cannot borrow `bar` as mutable more
                           // than once at a time [E0499]
    }

    fn f2() {
        let mut bar = Bar1 { x: Foo {} };
        bar.f(); // (2) 'bar' is not borrowed after the call
        let z = bar.f();  // ok (as expected)
    }
}
Run Code Online (Sandbox Code Playgroud)

那个不:

mod case2 {
    struct Foo {}

    struct Bar2<'b> {
        x: &'b Foo,
    }

    impl<'b> Bar2<'b> {
        fn f(&'b mut self) -> &'b Foo {
            self.x
        }
    }

    fn f4() {
        let foo = Foo {};
        let mut bar2 = Bar2 { x: &foo };
        bar2.f(); // (3) 'bar2' is borrowed as mutable, but who borrowed it?
        let z = bar2.f(); // error: cannot borrow `bar2` as mutable more than once at a time [E0499]
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望我可以在Bar2::f不刺激编译器的情况下调用两次,如案例1所示.

问题在于评论(3):谁借了bar2,而没有做作?

这是我的理解:

  1. 在情况1中,f2调用:lifetime参数'a是接收&Foo值之一,所以当没有做作时这个生命周期是空的,并且barBar1::f调用之后不被借用;

  2. 在情况2中,bar2借用foo(为不可改变),所以寿命参数'bBar2结构是foo参考有效时间,在结束时结束f4体.呼唤Bar2::f借用bar2那一生,即到终点f4.

但问题仍然是:谁借了bar2?可能Bar2::f吗?如何Bar2::f在通话后持有借来的所有权?我在这里错过了什么?

我在x86_64-pc-windows-msvc上使用Rust 1.14.0-nightly(86affcdf6 2016-09-28).

Sim*_*ead 9

如果是#2,你有这个:

impl<'b> Bar2<'b> {
    fn f(&'b mut self) -> &'b Foo {
        self.x
    }
}
Run Code Online (Sandbox Code Playgroud)

突出显示:&'b mut self&'b Foo指定相同的生命周期.

这就是说selfFoo两者实例的引用和返回引用具有相同的生命周期.查看呼叫站点,您有:

let foo = Foo {};
let mut bar2 = Bar2 { x: &foo };
Run Code Online (Sandbox Code Playgroud)

所以编译器推断都foobar2具有相同的寿命.生命周期foof4函数的范围,因此可变引用bar2分享了这一点.

解决此问题的一种方法是删除self引用上的显式生存期:

fn f(&mut self) -> &'b Foo
Run Code Online (Sandbox Code Playgroud)

这个编译并且编译器正确地理解了对不同生命周期的引用bar2和引用foo.

游乐场:https://play.rust-lang.org/? gist = caf262dd628cf14cc2884a3af842276a & version = stable & backtrace =0

TLDR:是的,在自引用上具有相同的生存期说明符,并且返回的引用意味着整个范围f4包含可变的借位bar2.


Mat*_* M. 9

啊......你基本上是自己借来的.

这个问题取决于你有相同的生命周期('b)用于生命周期Foo和生命周期Bar.编译器然后尽职尽责地统一这些生命周期,并且你最终处于一种奇怪的情况,在这种情况下,应该在语句结束处结束的借用的生命周期突然在值超出范围之后结束.

根据经验:始终使用新的一生self.别的什么都很奇怪.


有趣的是,这个模式实际上可能很有用(尽管更可能使用不可变借用):它允许锚定到堆栈帧,防止在调用函数之后的任何移动,这有时代表一个借用Rust没有很好地建模(比如将指针传递给FFI).