为什么还有一个借位呢?
为什么在下面的代码中调用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() } // ...
操场
我认为第二次借用应该没问题,因为f1
和f3
不是和f2
同时使用self
的。
这很烦人,但你可以通过引入一个内部范围并改变控制stream程来解决这个问题:
fn f3(&mut self) { { if let Some(x) = self.f1() { // ... return; } } self.f2() }
正如在评论中指出的,这个工作没有额外的大括号。 这是因为if
或if...let
expression式具有隐式范围,并且借用持续这个范围:
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中的生存时间参数化。
Chars
types也需要一个生命周期参数。 这意味着, Chars
types可能有一个成员元素,它需要一个生命周期参数。 生命期参数只对参考有意义(因为这里的生命实际上意味着“借用期限”)。
我们知道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); } }
我更新了使生命关系更清晰的代码。