无法移出可变引用后面的 ***

Шах*_*Шах 3 rust

我有下一个代码:

struct Tokenizer {
    reader: BufReader<File>,
    buf: Vec<u8>,
    token: String
}

impl Tokenizer {
    fn new(path: &PathBuf) -> Tokenizer {
        let file = File::open(path).expect("Unable to open file!");
        Tokenizer {
            reader: BufReader::new(file),
            buf: Vec::<u8>::new(),
            token: String::new()
        }
    }

    fn next(&mut self) -> bool {
        if self.buf.len() == 0 {
            if self.reader.read_until(b'\n', &mut self.buf)
                .expect("Unable to read file") == 0 {
                return false;
            }
        }
        let s = String::from_utf8(self.buf).expect("Unable to read file");
        let mut start: i8 = -1;
        let mut end: i8 = -1;
        for (i, c) in s.char_indices() {
            if start == -1 {
                if !c.is_whitespace() {
                    start = i as i8;
                }
            } else {
                if c.is_whitespace() {
                    end = i as i8;
                }
            }
        }
        self.token = s.chars().skip(start as usize).take((end - start) as usize).collect();
        self.buf = s.into_bytes();
        self.buf.clear();
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

但由于错误而不起作用:

error[E0507]: cannot move out of `self.buf` which is behind a mutable reference
  --> src\parser.rs:28:35
   |
28 |         let s = String::from_utf8(self.buf).expect("Unable to read file");
   |                                   ^^^^^^^^ move occurs because `self.buf` has type `std::vec::Vec<u8>`, which does not implement the `Copy` trait
Run Code Online (Sandbox Code Playgroud)

我读过类似的问题,建议使用,swap但我不知道它会如何帮助我。如何修复这个错误,为什么它不能编译?

Mas*_*inn 6

String::from_utf8按值获取其参数,这意味着它不仅仅使用它所消耗的 vec 。

但在这里它不能消耗输入:因为self只是一个可变借用self.buf最多是一个可变借用(又名&mut Vec<u8>),因此签名不匹配。这是因为String::from_utf8将检查输入是否有效,然后将其重新解释为有效字符串,避免新分配。

有两种简单的方法可以解决这个问题,我可以在这里看到:

最简单的方法是使用一个将引用作为输入的函数,例如str::from_utf8,这将在内部分配一个新的 String 以返回数据,因此效率稍低但更方便。

另一种选择——以及swap你得到的建议——是将数据移出借用:如果你有一个可变借用,你不能使用, self.buf但你可以用一个新的(拥有的)缓冲区替换它。

swap执行此操作并返回旧值,因此您可以将 的当前内容交换self.buf为全新的(空)向量,并取出以前称为 的向量self.buf,现在由您拥有(而不是self),因此可以使用。

它在这里的样子是:

        let mut buf = Vec::new();
        swap(&mut buf, self.buf);
        // here we now consume the swapped buf
        let s = String::from_utf8(buf).expect("Unable to read file");
        let mut start: i8 = -1;
        let mut end: i8 = -1;
        for (i, c) in s.char_indices() {
            if start == -1 {
                if !c.is_whitespace() {
                    start = i as i8;
                }
            } else {
                if c.is_whitespace() {
                    end = i as i8;
                }
            }
        }
        self.token = s.chars().skip(start as usize).take((end - start) as usize).collect();
        self.buf = s.into_bytes();
        self.buf.clear();
        return true;
Run Code Online (Sandbox Code Playgroud)

  • 我认为你对“str::from_utf8”的理解是错误的。据我所知,它不会进行任何新的分配或创建字符串,它只是将现有的内存切片重新解释为“str”切片。然而它仍然是“O(n)”,因为它扫描整个切片以检查它是否是有效的 utf8。有一个不安全版本“str::from_utf8_unchecked”,即“O(1)”,我会为任何相当大的字符串选择它。 (2认同)