Rust compiler has unintuitive lifetime for returned mutable borrow in loops
Pardon if I have an incorrect view of how the borrow/lifetime system works.
Compiler complains about multiple mutable borrows in a loop, if a mutable borrow can be returned from the function.
Consider the following:
use std::collections::VecDeque;
struct Something;
struct Struct1 {
list: VecDeque<usize>,
thing: Something,
}
impl Struct1 {
fn get_mut(&mut self) -> &mut Something {
for _ in self.list.iter() {
let a = &mut self.thing;
if true {
return a;
}
}
panic!("hi");
}
}
I get the following compilation
error[E0499]: cannot borrow `self.thing` as mutable more than once at a time
--> src/main.rs:20:26
|
20 | let a = &mut self.thing;
| ^^^^^^^^^^ mutable borrow starts here in previous iteration of loop
...
27 | }
| - mutable borrow ends here
...
error: aborting due to previous error
It is not expected to me that the lifetime of the mutable reference would clash. It seems that the only reason the lifetime of a is considered so long is because it can be returned. However, if it is being returned, I don't see why it should be considered a conflict, as it won't continue with the loop.
I'm not familiar with the details of the compiler, but I would expect, and I would want, the compiler to end the lifetime of the mutable borrow at the end of the loop. Or at the end of containing braces if there are any.
As far as I understand this should be fixed by non-lexical lifetimes; it's akin to problem 3 in the blog post.
Alex Burka at 2017-10-21 17:10:49
As of rust 1.39, is this problem resolved by nll ?
Alex.F at 2019-11-12 01:52:27
No, the original code still fails to compile.
8 | fn get_mut(&mut self) -> &mut Something { | - let's call the lifetime of this reference `'1` 9 | for _ in self.list.iter() { 10 | let a = &mut self.thing; | ^^^^^^^^^^^^^^^ mutable borrow starts here in previous iteration of loop 11 | if true { 12 | return a; | - returning this value requires that `self.thing` is borrowed for `'1`Alex Burka at 2019-11-13 01:28:46
Looks like this is similar to https://github.com/rust-lang/rust/issues/51132 which suggests it's addressed with polonius. And indeed, building with
-Zpolonius(rustc --versionisrustc 1.45.0-nightly (7ebd87a7a 2020-05-08)) looks to succeed:[19:17:37] # iximeow:~> rustc -Zpolonius --crate-type cdylib test_polonius2.rs warning: struct is never constructed: `Something` --> test_polonius2.rs:2:8 | 2 | struct Something; | ^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default warning: struct is never constructed: `Struct1` --> test_polonius2.rs:3:8 | 3 | struct Struct1 { | ^^^^^^^ warning: associated function is never used: `get_mut` --> test_polonius2.rs:8:5 | 8 | fn get_mut(&mut self) -> &mut Something { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: 3 warnings emittediximeow at 2020-08-30 02:19:19