为什么在迭代嵌套向量时会出现“一次不能多次借用 `*self` 作为可变变量”?

时间:2021-07-23 03:28:09

标签: rust

我想迭代两个嵌套向量 (Playground):

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(&mut self) -> Option<&[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(&mut self) -> Option<&[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(&mut self) -> Option<&[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 个答案:

答案 0 :(得分:2)

问题是 NLL 的 some parts 没有在编译器中实现,因为它们太占用 CPU。因此,编译器无法识别借用没有扩展到 match 之外。在一般情况下,解决方法是将其包装在一个无条件返回的 if 语句中。缺点是代码在运行时会变慢,因为它需要做两次相同的检查。这种机制在链接文档中得到了更好的解释。

 fn iter_record(&mut self) -> Option<&[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(),它只返回 truefalse,具体取决于 iter_record() 是否会返回 SomeNone 如果改为调用:

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

这将导致以下 working code

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(&mut self) -> Option<&[u8]> {
        self.index += 1;
        if self.index < self.data.len() {
            Some(self.data[self.index - 1].as_bytes())
        } else {
            None
        }
    }

    fn peek_record(&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(&mut self) -> Option<&[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(&mut self) -> Option<&[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
    }