Почему я получаю «не могу заимствовать» *self «как изменяемый более одного раза за раз» при повторении вложенного вектора?

#rust

Вопрос:

Я хочу повторить два вложенных вектора (игровая площадка):

 struct Name {
    index: usize,
    data: Vec<String>,
}

impl Name {
    fn new(test: bool) -> Option<Name> {
        if test {
            Some(Name {
                index: 0,
                data: vec![String::from("ATGCTA"), String::from("ACGTGA")],
            })
        } else {
            None
        }
    }
    fn iter_record(amp;mut self) -> Option<amp;[u8]> {
        self.index  = 1;
        if self.index < self.data.len() {
            Some(self.data[self.index - 1].as_bytes())
        } else {
            None
        }
    }
}

struct Data {
    index: usize,
    data: Vec<Option<Name>>,
}

impl Data {
    fn new() -> Data {
        Data {
            index: 0,
            data: vec![Name::new(true)],
        }
    }

    fn iter_record(amp;mut self) -> Option<amp;[u8]> {
        let max_index = self.data.len() - 1;
        let record = self.data[self.index].as_mut().unwrap().iter_record();
        match record {
            None => {
                if self.index < max_index {
                    self.index  = 1;
                    return self.iter_record();
                }
            }
            _ => {}
        }
        record
    }
}

fn main() {
    let mut data = Data::new();
    while let Some(ret) = data.iter_record() {
        println!("{:?}", ret);
    }
}
 

Вот ошибка сборки:

 error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:47:28
   |
40 |     fn iter_record(amp;mut self) -> Option<amp;[u8]> {
   |                    - let's call the lifetime of this reference `'1`
41 |         let max_index = self.data.len() - 1;
42 |         let record = self.data[self.index].as_mut().unwrap().iter_record();
   |                      --------- first mutable borrow occurs here
...
47 |                     return self.iter_record();
   |                            ^^^^ second mutable borrow occurs here
...
52 |         record
   |         ------ returning this value requires that `self.data` is borrowed for `'1`
 

Почему возникает эта ошибка? Как мне ее решить?

Комментарии:

1. нужно ли его вызывать рекурсивно? Вместо этого вы могли бы написать цикл while.

2. Я попробовал цикл, но получил аналогичную ошибку.

Ответ №1:

Проблема в том, что некоторые части NLL не были реализованы в компиляторе, потому что они слишком загружены процессором. В результате компилятор не может распознать, что заимствование не распространяется за пределы match . В общем случае обходной путь состоит в том, чтобы обернуть его в if оператор, который безоговорочно возвращает. Недостатком является то, что код будет работать медленнее во время выполнения, потому что ему нужно будет дважды выполнить одну и ту же проверку. Этот механизм гораздо лучше объяснен в связанном документе.

  fn iter_record(amp;mut self) -> Option<amp;[u8]> {
        let max_index = self.data.len() - 1;

        // This will not really work in your case, because you are modifying the struct's state, thus each method invocation will produce a different result
        let check = self.data[self.index].as_mut().unwrap().iter_record();
        if check.is_some() {
            match self.data[self.index].as_mut().unwrap().iter_record() {
                Some(r) => return Some(r),
                None => unreachable!(),
            }
        }

        if self.index < max_index {
            self.index  = 1;
            return self.iter_record();
        }

        None
    }
 

К сожалению, в вашем случае это не сработает, потому что вы Name::iter_record() изменяете внутреннее состояние структуры, поэтому ее невозможно вызвать дважды. Чтобы решить эту проблему, я представлю новый метод peek_record() , который просто вернется true или false , в зависимости от того iter_record() , вернулся Some бы или None был бы вызван вместо этого:

  fn peek_record(amp;self) -> bool {
        self.index   1 < self.data.len()
    }
 

Это приведет к следующему рабочему коду:

 struct Name {
    index: usize,
    data: Vec<String>,
}

impl Name {
    fn new(test: bool) -> Option<Name> {
        if test {
            Some(Name {
                index: 0,
                data: vec![String::from("ATGCTA"), String::from("ACGTGA")],
            })
        } else {
            None
        }
    }

    fn iter_record(amp;mut self) -> Option<amp;[u8]> {
        self.index  = 1;
        if self.index < self.data.len() {
            Some(self.data[self.index - 1].as_bytes())
        } else {
            None
        }
    }

    fn peek_record(amp;self) -> bool {
        self.index   1 < self.data.len()
    }
}

struct Data {
    index: usize,
    data: Vec<Option<Name>>,
}

impl Data {
    fn new() -> Data {
        Data {
            index: 0,
            data: vec![Name::new(true)],
        }
    }

    fn iter_record(amp;mut self) -> Option<amp;[u8]> {
        let max_index = self.data.len() - 1;

        if self.data[self.index].as_mut().unwrap().peek_record() {
            match self.data[self.index].as_mut().unwrap().iter_record() {
                Some(r) => return Some(r),
                None => unreachable!(),
            }
        }

        if self.index < max_index {
            self.index  = 1;
            return self.iter_record();
        }

        None
    }
}

fn main() {
    let mut data = Data::new();
    while let Some(ret) = data.iter_record() {
        println!("{:?}", ret);
    }
}
 

PS: Вы можете еще больше упростить свой код, избавившись от рекурсии:

     fn iter_record(amp;mut self) -> Option<amp;[u8]> {
        for idx in self.index..self.data.len() {
            if self.data[idx].as_mut().unwrap().peek_record() {
                match self.data[idx].as_mut().unwrap().iter_record() {
                    Some(r) => return Some(r),
                    None => unreachable!(),
                }
            }
            self.index  = 1;
        }

        None
    }
 

Комментарии:

1. Спасибо за ваше объяснение и код, в последнем блоке кода есть ошибка, self.index = 1; ее следует поместить в блок else.

2. @Moold ты прав. На самом self.index = 1; деле его можно переместить после if , потому что он всегда возвращается, когда он принимает значение true

3. «NLL» означает не лексическое время жизни .