Как назначить переменную, используемую в выражении соответствия внутри ветви соответствия? - PullRequest
0 голосов
/ 25 июня 2018

Я пытаюсь реализовать общую функцию join(), которая может работать на любом итераторе итераторов. У меня проблема с заемщиком в выражении match внутри реализации метода next(). Вот упрощенная версия моего кода:

pub struct Join<I>
where
    I: Iterator,
    I::Item: IntoIterator,
{
    outer_iter: I,
    inner_iter: Option<<I::Item as IntoIterator>::IntoIter>,
}

impl<I> Join<I>
where
    I: Iterator,
    I::Item: IntoIterator,
{
    pub fn new(mut iter: I) -> Join<I> {
        let inner_iter = iter.next().map(|it| it.into_iter());
        Join {
            outer_iter: iter,
            inner_iter,
        }
    }
}

impl<I> Iterator for Join<I>
where
    I: Iterator,
    I::Item: IntoIterator,
{
    type Item = <I::Item as IntoIterator>::Item;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match &mut self.inner_iter {
                Some(ref mut it) => match it.next() {
                    Some(x) => {
                        return Some(x);
                    }
                    None => {
                        self.inner_iter = self.outer_iter.next().map(|it| it.into_iter());
                    }
                },
                None => {
                    return None;
                }
            }
        }
    }
}

pub trait MyItertools: Iterator {
    fn join(self) -> Join<Self>
    where
        Self: Sized,
        Self::Item: IntoIterator,
    {
        Join::new(self)
    }
}

impl<I> MyItertools for I where I: Iterator {}

#[cfg(test)]
mod test {
    use super::MyItertools;

    #[test]
    fn it_works() {
        let input = [[1], [2]];
        let expected = [&1, &2];
        assert_eq!(input.iter().join().collect::<Vec<_>>(), expected);
    }
}

Текст ошибки:

error[E0506]: cannot assign to `self.inner_iter` because it is borrowed
  --> src/main.rs:39:25
   |
33 |             match &mut self.inner_iter {
   |                        --------------- borrow of `self.inner_iter` occurs here
...
39 |                         self.inner_iter = self.outer_iter.next().map(|it| it.into_iter());
   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.inner_iter` occurs here

Я понимаю, почему средство проверки заимствований жалуется на мой код, но я не нашел хорошего решения, только уродливый обходной путь:

fn next(&mut self) -> Option<Self::Item> {
    loop {
        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; }
        }
    }
}

Я полагаю, что подобные ситуации происходят регулярно; Как я могу переписать свой код, чтобы справиться с ними или избежать их?

Ответы [ 2 ]

0 голосов
/ 25 июня 2018

Вот более простое воспроизведение проблемы:

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

Это слабость проверки заимствований на основе AST.Когда нелексические времена жизни включены, это работает как есть .Усовершенствованная проверка заимствований на основе MIR позволяет увидеть, что в точке, в которой вы пытаетесь заменить ее, нет заимствований соответствующей переменной.


Для чего бы вы ни стоили, ваш join простоflat_map:

input.iter().flat_map(|x| x)

Или flatten:

input.iter().flatten()

Вы можете увидеть, как эти реализуютnext для другой идеи:

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,
        }
    }
}

Это ясно показывает, что значение итератора не действительно заимствует у inner_iter.


Не глядя на flatten, я бы предпочел четко указать, что нет никакого дублирующего заимствования, если взять Option и восстановить его, если оно равно Some, как вы сделали:

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 голосов
/ 25 июня 2018

В подобных ситуациях я считаю полезным написать код из двух частей: сначала собрать данные, а затем обновить изменяемый:

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;
    }
}

Тот факт, что все ветви, которые не изменяют inner_iter, делают return, облегчает код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...