How to assign to the variable used in match expression inside a match branch?

前端 未结 2 390
再見小時候
再見小時候 2021-01-27 04:25

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

相关标签:
2条回答
  • 2021-01-27 05:14

    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;
        }
    }
    
    0 讨论(0)
  • 2021-01-27 05:32

    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.

    0 讨论(0)
提交回复
热议问题