Why is the loan still held in the else block, if any?

Why is the call to self.f2() in the following code self.f2() ? Isn't the else block in another area? This is quite a mystery!

 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; } 

Playground

 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 

Doesn't borrowing for yourself start and end self.f1() ? Once the call from f1() returned f1() no longer uses self, so the borrower should not have a problem with the second borrowing. Please note that the following code also does not work ...

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

Playground

I think the second borrowing should be fine, since f1 and f3 do not use self at the same time as f2 .

+9
source share
5 answers

This is annoying, but you can get around this by introducing an inner area and slightly changing the control flow:

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

As stated in the comments, this works without additional braces. This is due to the fact that the expression if or if...let has an implicit volume, and the occupation lasts for this area:

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

Here's the IRC chat log between Sandeep Datta and mbrubeck:

mbrubeck: std: tr :: Chars contains a borrowed link to the line that created it. Full name of type Chars<'a> . So f1(&mut self) -> Option<Chars> without permission f1(&'a mut self) -> Option<Chars<'a>> means that self remains borrowed as long as the return value from f1 is in scope.

Sandeep Datta: Can I use 'b for myself and' a for Chars to avoid this problem?

mbrubeck: No, if you are actually returning an iterator over something from self . Although if you can make a function from &self -> Chars (instead of &mut self -> Chars ), that will fix the problem.

+4
source

I put together an example to show the review rules:

 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"); } } 

Fingerprints:

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

In short, it seems that the expression in the if lives on both the if block and the else block.

On the one hand, this is not surprising, since it actually takes longer to live than the if block, but on the other hand, it really prevents the use of useful templates.

If you prefer a visual explanation:

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

desugars in:

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

while you prefer it to desugars in:

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

You are not the first one to stumble, so at some point this can be fixed, despite changing the value of some code (in particular, the guards).

+9
source

The modified link is a very strong guarantee: there is only one pointer to a specific memory location. Since you already have one loan &mut , you also cannot have a second. This will lead to the appearance of a data race in a multi-threaded context and the invalidity of an iterator and other similar problems in a single-threaded context.

Right now, borrowing is based on the lexical field, and so the first debt lasts until the end of the function, period. In the end, we hope to mitigate this limitation, but it will take some work.

+3
source

Here's how you can get rid of false errors. I am new to Rust, so serious errors may occur in the following explanation.

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

'a Here is the life parameter (as well as the template parameters in C ++). Types can be parameterized by the lifetime in Rust.

The Chars type also takes a lifetime parameter. This implies that the Chars type probably has a member element that requires the lifetime parameter. The lifetime parameters are meaningful only in links (since the lifetime here means "borrowing life").

We know that Chars needs to keep a reference to the line from which it was created, 'a will probably be used to indicate the lifetime of the original line.

Here, we simply supply 'a as a parameter to the Chars lifetime, telling the Rust compiler that the lifetime of the Chars same as the lifetime of struct A The lifetime of the IMO "a type A" should be read as the "lifetime" of the references contained in structure A ".

I think that the implementation of the structure can be parameterized regardless of the structure itself, so we need to repeat the parameters using the impl . Here we bind the name 'a to the lifetime of structure A.

 impl<'a> A<'a> { 

The name 'b is entered in the context of the function f2 . Here it is used to bind the &mut self link over time.

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

The name 'b is entered in the context of the function f1 . This 'b not directly related to 'b introduced above f2 .

Here it is used to bind the &mut self link over time. Needless to say, this link also has nothing to do with &mut self in the previous function, this is a new independent self loan.

If we had not used the explicit annotation of life expectancy, here Rust would have used its rules for eliminating life to get the next signature of the function ...

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

As you can see, this binds the lifetime of the &mut self reference parameter to the lifetime of the Chars object returned from this function (this Chars object should not be the same as self.chars ), this is absurd, since the returned Chars will survive the &mut self link . Therefore, we need to separate the two lifetimes as follows:

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

Remember &mut self is a loan self , and all that &mut self is mentioned is also a loan. Therefore, we cannot return Some(self.chars) here. self.chars not ours to give (Error: cannot exit borrowed content.).

We need to create a self.chars clone self.chars that it can be issued.

 Some(self.chars.clone()) 

Note that the returned Chars has the same lifetime as structure A.

And now f3 without changes and without compilation errors!

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

The main function is for completeness only ...

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

I updated the code to make life expectancy relationships clearer.

+3
source

Starting in Rust 2018, the source code will work as is . This is due to the fact that Rust 2018 allows non-lexical times of life .

0
source

Source: https://habr.com/ru/post/987217/


All Articles