Background
I know that the borrow checker disallows more than one mutable borrows. For example, the code below is invalid:
fn main() {
let mut x = 42;
let a = &mut x;
let b = &mut x;
println!("{} {}", a, b);
}
However, if the first borrow is dropped due to out of scope, the second borrow is valid:
fn main() {
let mut x = 1;
{
let a = &mut x;
println!("{}", a);
}
let b = &mut x;
println!("{}", b);
}
Because of non-lexical lifetimes (NLL), the first borrow doesn't even have to be out of scope — the borrow checker only requires it not to be used anymore. So, the code below is valid in Rust 2018:
fn main() {
let mut x = 1;
let a = &mut x;
println!("{}", a);
let b = &mut x;
println!("{}", b);
}
The Problem
But I don't understand why the code below is invalid:
use std::str::Chars;
fn main() {
let s = "ab".to_owned();
let mut char_iter = s.chars();
let mut i = next(&mut char_iter);
dbg!(i.next());
let mut j = next(&mut char_iter);
dbg!(j.next());
}
fn next<'a>(char_iter: &'a mut Chars<'a>) -> impl Iterator<Item = char> + 'a {
char_iter.take_while(|&ch| ch != ' ')
}
Compile error message:
error[E0499]: cannot borrow `char_iter` as mutable more than once at a time
--> src/main.rs:10:22
|
7 | let mut i = next(&mut char_iter);
| -------------- first mutable borrow occurs here
...
10 | let mut j = next(&mut char_iter);
| ^^^^^^^^^^^^^^ second mutable borrow occurs here
11 | dbg!(j.next());
12 | }
| - first borrow might be used here, when `i` is dropped and runs the destructor for type `impl std::iter::Iterator`
From the error message I thought that maybe NLL doesn't support this case yet. So, I dropped i
early:
use std::str::Chars;
fn main() {
let s = "ab".to_owned();
let mut char_iter = s.chars();
{
let mut i = next(&mut char_iter);
dbg!(i.next());
}
let mut j = next(&mut char_iter);
dbg!(j.next());
}
fn next<'a>(char_iter: &'a mut Chars<'a>) -> impl Iterator<Item = char> + 'a {
char_iter.take_while(|&ch| ch != ' ')
}
But I got a more confusing error message:
error[E0499]: cannot borrow `char_iter` as mutable more than once at a time
--> src/main.rs:12:22
|
8 | let mut i = next(&mut char_iter);
| -------------- first mutable borrow occurs here
...
12 | let mut j = next(&mut char_iter);
| ^^^^^^^^^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
Why does it say first borrow later used here
even if i
is already dropped and out of scope before?
An Alternative Approach
The code is compiled if I change the signature of next
function into this:
fn next(char_iter: impl Iterator<Item = char>) -> impl Iterator<Item = char> {
char_iter.take_while(|&ch| ch != ' ')
}
But still, I want to understand why the original next
function doesn't work.
impl
trait can be used in type parameters. I previously thought it can only be used in argument or return types as per the official doc. – Marlin