Rust: &mut自我借贷与自身发生冲突。

创建于 2015-02-03  ·  20评论  ·  资料来源: rust-lang/rust

我不知道为什么会这样,但是IRC上的rovar和XMPPwocky认为这是编译器错误。

struct A {
    a: i32
}

impl A {
    fn one(&mut self) -> &i32{
        self.a = 10;
        &self.a
    }
    fn two(&mut self) -> &i32 {
        loop {
            let k = self.one();
            if *k > 10i32 {
                return k;
            }
        }
    }
}

出现以下错误...

<anon>:12:21: 12:25 error: cannot borrow `*self` as mutable more than once at a time
<anon>:12             let k = self.one();
                              ^~~~
<anon>:12:21: 12:25 note: previous borrow of `*self` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*self` until the borrow ends
<anon>:12             let k = self.one();
                              ^~~~
<anon>:17:6: 17:6 note: previous borrow ends here
<anon>:10     fn two(&mut self) -> &i32 {
...
<anon>:17     }
              ^
error: aborting due to previous error
playpen: application terminated with error code 101

有趣的是,如果第二种方法更改为...

    fn two(&mut self) -> &i32 {
        loop {
            let k = self.one();
            return k;
        }
    }

这样可以编译良好。

围栏: http :

A-NLL A-borrow-checker A-typesystem C-bug NLL-polonius T-compiler

最有用的评论

我认为我在生产中遇到了类似的问题,一成不变的借贷似乎寿命太长解决方法)。

我还应该提到, starwed的代码不再在最新的夜间编译。 @oberien建议使用“ regression-from-nightly-to-nightly ”标记,还建议我应该标记@nikomatsakis ,以防万一这是一个紧急问题:)

所有20条评论

我相信这是正确的行为。 要了解原因,请考虑带有显式生命周期注释的代码:

struct A {
    a: i32
}

impl A {
    fn one<'a>(&'a mut self) -> &'a i32{
        self.a = 10;
        &self.a
    }
    fn two<'a>(&'a mut self) -> &'a i32 {
        loop {
            let k = self.one();
            if *k > 10i32 {
                return k;
            }
        }
    }
}

two需要返回一个引用,该引用的生存期与其给定的可变借用寿命相同。 但是,因为它得到了。 这意味着k必须具有生存期'a 。 但是,对于返回值one具有生存期'a ,向one的输入也必须具有生存期'a 。 这意味着Rust被迫不“重新借入” self并将其移入one 。 由于可变引用是线性的,因此只能移动一次,但是loop意味着可能需要重复使用相同的可变引用来调用one

第二个示例仅起作用,因为Rust可以看到该循环仅运行一次,因此self将仅移动一次。

这看起来像是非词汇借用问题。 您可以获得完全没有任何循环的类似行为:

struct Foo { data: Option<i32> }

fn main() {
    let mut x = Foo{data: Some(1)};

    foo(&mut x);
}

fn foo(x: &mut Foo) -> Option<&mut i32> {
    if let Some(y) = x.data.as_mut() {
        return Some(y);
    }

    println!("{:?}", x.data); 
    None
}
<anon>:14:22: 14:28 error: cannot borrow `x.data` as immutable because it is also borrowed as mutable
<anon>:14     println!("{:?}", x.data); 
                               ^~~~~~
note: in expansion of format_args!
<std macros>:2:43: 2:76 note: expansion site
<std macros>:1:1: 2:78 note: in expansion of println!
<anon>:14:5: 14:30 note: expansion site
<anon>:10:22: 10:28 note: previous borrow of `x.data` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `x.data` until the borrow ends
<anon>:10     if let Some(y) = x.data.as_mut() {
                               ^~~~~~
<anon>:16:2: 16:2 note: previous borrow ends here
<anon>:9 fn foo(x: &mut Foo) -> Option<&mut i32> {
...
<anon>:16 }
          ^
error: aborting due to previous error

Rustc不能通过有条件的借款收益来“交易”。

CC @pcwalton

我认为问题实际上是不是使用了返回值的可变性,而是使用了函数参数的可变性。

以下代码调用一个变异函数,该函数返回一个不变的引用。 在那之后,我不能接受不可变的引用。

struct Foo (u8);

impl Foo {
    fn bar(&mut self) -> &u8 {
        self.0 += 1;
        &self.0
    }
}

fn main() {
    let mut x = Foo(42);
    let a = x.bar(); // note: borrow of `x` occurs here
    let b = x.0; // error: cannot use `x.0` because it was mutably borrowed
}

游戏围栏

我仍然在最近的每晚上遇到这个问题。 看起来,即使没有返回值的函数通过借位检查器,它也将返回视为具有生存期,直到函数结束。 考虑到返回点如何尽早结束函数,这似乎有些奇怪。

如果您看一下@Gankro的示例,即使您删除了return关键字,它也会通过借位检查器,即使结果函数无法正常工作。

这在非词法生存期内被阻止,而在词法生存期内被阻止。 理想情况下,此问题将在2016年底前解决,但很快就不会修复。

关于内部的一些讨论: https :

非词汇借用功能讨论: https :

因此,阅读[内部线程]使我感到这是不可修复的(即使使用NLL也是如此)。 有人可以确认吗? (@eddyb?)

cc @休息长/长

这确实是NLL,如果我没记错的话,它将由我提出的各种建议解决。 这大致对应于我的第一篇博文中的“问题案例3”。 为何可行的基本思想(根据最新博客文章的表述)是,只需要将借入的子类型扩展到if的分支中'a的末尾。 if导致return

@nikomatsakis您能否详细说明NLL与该问题的相互作用? 在&mut引用的心理模型中,可以通过movereborrow传递它们,而本期中的问题是返回需要使用move模式,并重复使用self之后需要重新借用。 据我了解,新借入的&mut引用的生存期受持有&mut -引用的变量的生存期限制–在本例中self变量,因此受以下限制函数的主体,因此它不能扩展到函数调用之外。 NLL是否会改变重新借贷的限制(或者可能没有这种限制)?

我也想知道解决这个问题是与NLL固有联系在一起的,还是正交的? 如果是后者,也许值得在NLL之前发布修复程序?

另外,如果NLL要解决此问题,是否意味着在NLL下您将不必手动在移动和重新借入之间进行选择?

这会很快解决吗?

@krdln

您能否详细说明NLL如何解决此问题?

总结是,今天,如果在任何路径上从函数中返回一个值,则借出必须在所有路径上对该函数的其余部分有效。 根据我提出的NLL提案,该限制已解除。

更详细地讲,如果您在此处采用详细的签名,则可以看到我们必须返回带有生存期'a的引用:

fn two<'a>(&'a mut self) -> &'a i32 { .. }

现在观察我们调用let k = self.one() ,然后返回此k 。 这意味着k必须为&'a i32 。 这依次意味着,当我们调用self.one() ,必须向其提供self的借项,类型&'a mut Self 。 因此,我们以let k = self.one()发放self的贷款,有效期'a 。 这个生存期大于循环(乃至整个函数),因此当我们绕过循环时会持续存在,从而导致错误报告。 根据我提议的NLL规则,如果采用ifk的生存期仅需要延长到'a 。 因此,如果未提取if ,则贷款可以提前结束。

有帮助吗?


@ osa1

这会很快解决吗?

我们尚需完成一些步骤,但仍在积极开展必要的重构工作。

@nikomatsakis
谢谢,它有所帮助。 但是,有一件我不理解的小事情-当您在代码中写入self ,会发生什么- self的重新借贷是如何工作的。 我已经读过您对“问题案例3”( get_default )的解释,在这里您可以内联调用方中的代码,但是在那里,您已经将self每次使用都改为借用了map直接变量,所以不了解就不会为我清除它。

这就是我遇到的问题:当我们调用let k = self.one()self无法移动(因为以后需要使用),因此它被认为是借来的。 稍后,我们有条件地返回该k ,以便借用必须具有生存期'a ,该生存期比函数调用长。 但! 我们是从self借用的,该生存期一直到函数结束。 这个限制似乎缩短了'a ,所以在我的心理模型中,即使在NLL下,我们也应该得到“寿命不足”错误。

@krdln实际上是我们从*self借来的-也就是说,我们重新借了self所指的东西。 我们允许您借用*self一辈子,而不是self本身,因为self意味着整个生命周期'a的“锁定”。 因此,只要我们可以保证在'a期间不再使用self,则结果应为排他性引用-在这种情况下,返回fn后,self被弹出,因此可以不要使用self,因此只要保证在fn结束之前不使用self就可以了。 (至少我希望是这样。=)

这在非词法生存期内被阻止,而在词法生存期内被阻止。 理想情况下,此问题将在2016年底前解决,但很快就不会修复。

并不奇怪,但是我确认,如果打开非词条生命周期功能,则最初的示例确实会在每晚编译

我认为我在生产中遇到了类似的问题,一成不变的借贷似乎寿命太长解决方法)。

我还应该提到, starwed的代码不再在最新的夜间编译。 @oberien建议使用“ regression-from-nightly-to-nightly ”标记,还建议我应该标记@nikomatsakis ,以防万一这是一个紧急问题:)

偶然发现此问题,尝试执行非常类似的操作(当前nll不允许这样做):

fn f(vec: &mut Vec<u8>) -> &u8 {
    if let Some(n) = vec.iter_mut().find(|n| **n == 1) {
        *n = 10;
        n
    } else {
        vec.push(10);
        vec.last().unwrap()
    }
}

fn main() {
    let mut vec = vec![1, 2, 3];
    f(&mut vec);
}
error[E0499]: cannot borrow `*vec` as mutable more than once at a time
 --> src/main.rs:6:9
  |
1 | fn f(vec: &mut Vec<u8>) -> &u8 {
  |           - let's call the lifetime of this reference `'1`
2 |     if let Some(n) = vec.iter_mut().find(|n| **n == 1) {
  |                      --- first mutable borrow occurs here
3 |         *n = 10;
4 |         n
  |         - returning this value requires that `*vec` is borrowed for `'1`
5 |     } else {
6 |         vec.push(10);
  |         ^^^ second mutable borrow occurs here

error[E0502]: cannot borrow `*vec` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:9
  |
1 | fn f(vec: &mut Vec<u8>) -> &u8 {
  |           - let's call the lifetime of this reference `'1`
2 |     if let Some(n) = vec.iter_mut().find(|n| **n == 1) {
  |                      --- mutable borrow occurs here
3 |         *n = 10;
4 |         n
  |         - returning this value requires that `*vec` is borrowed for `'1`
...
7 |         vec.last().unwrap()
  |         ^^^ immutable borrow occurs here

(我们在删除NLL-fixed-by-NLL时应该删除了E-needstest)

此页面是否有帮助?
0 / 5 - 0 等级