问题
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 != ' ')
}
(Rust Playground)
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.
回答1:
Let's decipher this type deduction magic here. impl Iterator
is actually a concrete type: Chars
that wrapped with TakeWhile
, so you may rewrite your method like this (btw, an interesting task is to determine the &char
's lifetime):
fn next<'a>(
char_iter: &'a mut Chars<'a>,
) -> TakeWhile<&'a mut Chars<'a>, impl FnMut(&char) -> bool> {
char_iter.take_while(|&ch| ch != ' ')
}
Now you may see that the output type lives as long as the input and vice versa. That lifetime, in fact, derived from the &str
that is originally used. Therefore, you may conclude that the resulted type lives as long as the used string (i.e. to the end of the main
) and even explicit drop(i)
won't help you, because compiler knows that the Chars
is borrowed till the end. For working nll you must (unfortunately?) help the compiler:
fn next<'a, 'b: 'a>(
char_iter: &'a mut Chars<'b>,
) -> TakeWhile<&'a mut Chars<'b>, impl FnMut(&char) -> bool> {
char_iter.take_while(|&ch| ch != ' ')
}
回答2:
The problem is that you're explicitly telling the borrow checker that i
lives as long as char_iter
in the following block by declaring both to have the same lifetime 'a
.
fn next<'a>(char_iter: &'a mut Chars<'a>) -> impl Iterator<Item = char> + 'a {
char_iter.take_while(|&ch| ch != ' ')
}
This means that the compiler believes that &mut char_iter
is still in use as long as char_iter
is still in scope. That is, until the end of main()
.
来源:https://stackoverflow.com/questions/62817320/why-does-the-borrow-checker-disallow-a-second-mutable-borrow-even-if-the-first-o