【问题标题】:Cannot move out of *** which is behind a mutable reference [duplicate]无法移出可变引用后面的 *** [重复]
【发布时间】:2020-11-30 20:36:34
【问题描述】:

我有下一个代码:

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

但是由于错误而不起作用:

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

我读过类似的问题,建议使用swap,但我不知道它对我有什么帮助。如何修复此错误以及为什么无法编译?

【问题讨论】:

    标签: rust


    【解决方案1】:

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

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

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

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

    另一种选择——以及你得到的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;
    

    附录:从 1.40 开始,还有一个 mem::take 实用函数,它将简单地将值与其 默认值 交换(因此仅适用于 Default 类型)。

    由于Vec&lt;u8&gt;Default,你可以替换

            let mut buf = Vec::new();
            swap(&mut buf, self.buf);
    

            let buf = take(self.buf);
    

    【讨论】:

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

    String::from_utf8 试图获取它的参数的所有权时,您会收到此错误。在您的情况下,它是 &amp;mut self 的属性,被引用(您计划在将来使用此结构,而不是使用它)。

    简单的解决方案是使用其他引用的函数:

    fn next(&mut self) -> bool {
        ...
        let s = String::from_utf8_lossy(&self.buf[..]);
        ...
    }
    

    并删除 self.buf = s.into_bytes(); 行,因为你是 1. 在下一行清理它; 2. 没有被消费,而是被引用。

    【讨论】:

    • 在这种特殊情况下,我认为str::from_utf8 会更合适。
    • @rodrigo 这是一个非消费函数的例子;)
    • 感谢您的回答,我会尝试使用您的变体而无需应对。
    猜你喜欢
    • 1970-01-01
    • 2020-09-11
    • 1970-01-01
    • 2020-02-12
    • 2015-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多