I\'m trying to implement a general function join()
that can work on any iterator of iterators. I have a problem with the borrow checker in a match
expr
Here is a simpler reproduction of the problem:
fn main() {
let mut a = (42, true);
match a {
(ref _i, true) => a = (99, false),
(ref _i, false) => a = (42, true),
}
println!("{:?}", a);
}
error[E0506]: cannot assign to `a` because it is borrowed
--> src/main.rs:4:27
|
4 | (ref _i, true) => a = (99, false),
| ------ ^^^^^^^^^^^^^^^ assignment to borrowed `a` occurs here
| |
| borrow of `a` occurs here
error[E0506]: cannot assign to `a` because it is borrowed
--> src/main.rs:5:28
|
5 | (ref _i, false) => a = (42, true),
| ------ ^^^^^^^^^^^^^^ assignment to borrowed `a` occurs here
| |
| borrow of `a` occurs here
This is a weakness of the AST-based borrow checker. When non-lexical lifetimes are enabled, this works as-is. The enhanced MIR-based borrow checker can see that there's no borrow of the matched-on variable at the point at which you try to replace it.
For what it's worth, your join
is just a flat_map:
input.iter().flat_map(|x| x)
Or a flatten:
input.iter().flatten()
You can see how these implement next for another idea:
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(v) = self.inner_iter.as_mut().and_then(|i| i.next()) {
return Some(v);
}
match self.outer_iter.next() {
Some(x) => self.inner_iter = Some(x.into_iter()),
None => return None,
}
}
}
This clearly delineates that the iterator value doesn't really borrow from inner_iter
.
Without looking at flatten
, I would have chosen to clearly indicate that there's no overlapping borrowing by taking the Option
and restoring it if it is Some
, as you've done:
match self.inner_iter.take() {
Some(mut it) => match it.next() {
Some(x) => {
self.inner_iter = Some(it);
return Some(x);
}
None => {
self.inner_iter = self.outer_iter.next().map(|it| it.into_iter());
}
},
None => {
return None;
}
}
In these kind of situations, I find useful to write the code in two pieces: first collect the data, then update the mutable:
fn next(&mut self) -> Option<Self::Item> {
loop {
//collect the change into a local variable
let ii = match &mut self.inner_iter {
Some(ref mut it) => {
match it.next() {
Some(x) => { return Some(x); }
None => self.outer_iter.next().map(|it| it.into_iter())
}
}
None => { return None; }
};
//self.inner_iter is no longer borrowed, update
self.inner_iter = ii;
}
}
The fact that all the branches that do not modify the inner_iter
do a return
makes the code easier.