我不知道为什么会这样,但是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 :
我相信这是正确的行为。 要了解原因,请考虑带有显式生命周期注释的代码:
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 @休息长/长
@nikomatsakis您能否详细说明NLL与该问题的相互作用? 在&mut
引用的心理模型中,可以通过move或reborrow传递它们,而本期中的问题是返回需要使用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规则,如果采用if
则k
的生存期仅需要延长到'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)
最有用的评论
我认为我在生产中遇到了类似的问题,一成不变的借贷似乎寿命太长(解决方法)。
我还应该提到, starwed的代码不再在最新的夜间编译。 @oberien建议使用“
regression-from-nightly-to-nightly
”标记,还建议我应该标记@nikomatsakis ,以防万一这是一个紧急问题:)