为什么还有一个借位呢?

为什么在下面的代码中调用self.f2()是为了借阅检查器? 不是在不同的范围内的其他块? 这是一个相当难题!

 use std::str::Chars; struct A; impl A { fn f2(&mut self) {} fn f1(&mut self) -> Option<Chars> { None } fn f3(&mut self) { if let Some(x) = self.f1() { } else { self.f2() } } } fn main() { let mut a = A; } 

操场

 error[E0499]: cannot borrow `*self` as mutable more than once at a time --> src/main.rs:16:13 | 13 | if let Some(x) = self.f1() { | ---- first mutable borrow occurs here ... 16 | self.f2() | ^^^^ second mutable borrow occurs here 17 | } | - first borrow ends here 

self.f1()调用的自我借用的范围不是开始和结束的吗? 一旦来自f1()的调用已经返回f1()不再使用自己,因此借用检查器应该没有第二次借用的任何问题。 请注意以下代码也会失败

 // ... if let Some(x) = self.f1() { self.f2() } // ... 

操场

我认为第二次借用应该没问题,因为f1f3不是和f2同时使用self的。

这很烦人,但你可以通过引入一个内部范围并改变控制stream程来解决这个问题:

 fn f3(&mut self) { { if let Some(x) = self.f1() { // ... return; } } self.f2() } 

正如在评论中指出的,这个工作没有额外的大括号。 这是因为ifif...letexpression式具有隐式范围,并且借用持续这个范围:

 fn f3(&mut self) { if let Some(x) = self.f1() { // ... return; } self.f2() } 

以下是Sandeep Datta和mbrubeck之间的IRC聊天logging:

mbrubeck: std:tr :: Chars包含对创build它的string的借用引用。 完整的types名称是Chars<'a> 。 所以f1(&mut self) -> Option<Chars> without elision是f1(&'a mut self) -> Option<Chars<'a>>这意味着只要f1的返回值在范围内, 。

Sandeep Datta:我可以用“b”作为自己,“chars”可以用来避免这个问题?

mbrubeck:不是,如果你实际上是从self返回一个迭代器。 虽然如果你可以用&self -> Chars (而不是&mut self -> Chars )来解决这个问题。

我举了一个例子来展示范围规则:

 struct Foo { a: i32, } impl Drop for Foo { fn drop(&mut self) { println!("Foo: {}", self.a); } } fn generate_temporary(a: i32) -> Option<Foo> { if a != 0 { Some(Foo { a: a }) } else { None } } fn main() { { println!("-- 0"); if let Some(foo) = generate_temporary(0) { println!("Some Foo {}", foo.a); } else { println!("None"); } println!("-- 1"); } { println!("-- 0"); if let Some(foo) = generate_temporary(1) { println!("Some Foo {}", foo.a); } else { println!("None"); } println!("-- 1"); } { println!("-- 0"); if let Some(Foo { a: 1 }) = generate_temporary(1) { println!("Some Foo {}", 1); } else { println!("None"); } println!("-- 1"); } { println!("-- 0"); if let Some(Foo { a: 2 }) = generate_temporary(1) { println!("Some Foo {}", 1); } else { println!("None"); } println!("-- 1"); } } 

这打印:

 -- 0 None -- 1 -- 0 Some Foo 1 Foo: 1 -- 1 -- 0 Some Foo 1 Foo: 1 -- 1 -- 0 None Foo: 1 -- 1 

总之,似乎if语句中的expression式通过if块和else块都存在。

一方面,这并不奇怪,因为它的确需要比if区块更长的寿命,但另一方面确实阻止了有用的模式。

如果你喜欢一个视觉解释:

 if let pattern = foo() { if-block } else { else-block } 

parsing成:

 { let x = foo(); match x { pattern => { if-block } _ => { else-block } } } 

而你更喜欢它parsing成:

 bool bypass = true; { let x = foo(); match x { pattern => { if-block } _ => { bypass = false; } } } if not bypass { else-block } 

你不是第一个被这个绊倒的人,所以这个问题可能会在某些时候被解决,尽pipe改变了一些代码的含义(特别是守卫)。

可变引用是一个非常有力的保证:只有一个指向特定内存位置的指针。 既然你已经有了一个借口,你也不&mut有一个借口了。 这将在multithreading上下文中引入数据竞争,并在单线程上下文中引入迭代器失效和其他类似问题。

目前,借用是基于词汇范围的,所以第一次借款持续到函数结束时期。 最终,我们希望放宽这个限制,但这需要一些工作。

这里是你如何摆脱虚假的错误。 我是Rust新手,所以在下面的解释中可能会有严重的错误。

 use std::str::Chars; struct A<'a> { chars: Chars<'a>, } 

'a这里是一个生命周期参数(就像C ++中的模板参数一样)。 types可以通过在Rust中的生存时间参数化。

Charstypes也需要一个生命周期参数。 这意味着, Charstypes可能有一个成员元素,它需要一个生命周期参数。 生命期参数只对参考有意义(因为这里的生命实际上意味着“借用期限”)。

我们知道Chars需要保持对创build它的string的引用, 'a可能会被用来表示源string的生命周期。

在这里,我们简单地提供'a作为生命周期参数给Chars告诉Rust编译器, Chars的生命周期和struct A的生命周期是一样A 。 国际海事组织“一生的atypes”应被视为包含在结构A“参考”的“生命”a。

我认为结构实现可以独立于结构本身进行参数化,因此我们需要用impl关键字重复参数。 在这里,我们把名字'a绑定到struct A的生命周期。

 impl<'a> A<'a> { 

名称'b是在函数f2的上下文中引入的。 这里用于绑定参考&mut self的生命周期。

 fn f2<'b>(&'b mut self) {} 

名称'b是在函数f1的上下文中引入的。'b 'b与上面的f2引入的'b没有直接关系。

这里用于绑定参考&mut self的生命周期。 不用说,这个引用也&mut self上一个函数中的&mut self没有任何关系,这是一个新的独立的self借用。

如果我们没有在这里使用明确的生命周期标注,Rust会使用它的生命周期规则来得到下面的函数签名。

 //fn f1<'a>(&'a mut self) -> Option<Chars<'a>> 

正如你所看到的,这&mut self参考&mut self参数的生命周期绑定到从这个函数返回的Chars对象的生命周期(这个Chars对象不需要和self.chars ),这是荒谬的,因为返回的Chars会比&mut self引用。 因此,我们需要将两个生命周期分开如下:

 fn f1<'b>(&'b mut self) -> Option<Chars<'a>> { self.chars.next(); 

记住&mut self&mut self借用,而&mut self所提及的任何东西也是一种借用。 因此,我们不能在这里返回Some(self.chars)self.chars是不是我们的(错误:不能移出借来的内容)。

我们需要创buildself.chars一个克隆,以便它可以被发出。

 Some(self.chars.clone()) 

注意这里返回的Chars与struct A具有相同的生命周期。

现在这里是f3不变,没有编译错误!

 fn f3<'b>(&'b mut self) { if let Some(x) = self.f1() { //This is ok now } else { self.f2() //This is also ok now } } 

主要function只是为了完整性…

 fn main() { let mut a = A { chars:"abc".chars() }; a.f3(); for c in a.chars { print!("{}", c); } } 

我更新了使生命关系更清晰的代码。