Pee*_*kmo 115 reference move-semantics rust borrow-checker
我不明白这个错误cannot move out of borrowed content.我收到了很多次,我总是解决它,但我从来没有理解为什么.
例如:
for line in self.xslg_file.iter() {
    self.buffer.clear();
    for current_char in line.into_bytes().iter() {
        self.buffer.push(*current_char as char);
    }
    println!("{}", line);
}
产生错误:
error[E0507]: cannot move out of borrowed content
  --> src/main.rs:31:33
   |
31 |             for current_char in line.into_bytes().iter() {
   |                                 ^^^^ cannot move out of borrowed content
我通过克隆解决了这个问题line:
error[E0507]: cannot move out of `*line` which is behind a shared reference
  --> src/main.rs:31:33
   |
31 |             for current_char in line.into_bytes().iter() {
   |                                 ^^^^ move occurs because `*line` has type `std::string::String`, which does not implement the `Copy` trait
即使在阅读其他帖子后我也不明白错误:
这种错误的起源是什么?
She*_*ter 98
让我们来看看签名into_bytes:
fn into_bytes(self) -> Vec<u8>
这需要self,而不是对self(&self)的引用.这意味着self将被消费,并且在通话后将无法使用.在它的位置,你得到一个Vec<u8>.前缀into_是表示这样的方法的常用方法.
我不知道你的iter()方法究竟返回了什么,但我的猜测是它是一个迭代器&String,也就是说,它返回对a的引用String但不给你所有权.这意味着您无法调用消耗该值的方法.
正如您所发现的,一种解决方案是使用clone.这将创建一个重复的对象做自己的,并可以调用into_bytes上.正如其他评论者所提到的那样,你也可以使用as_bytes哪个&self,所以它将使用借来的价值.您应该使用哪一个取决于您使用指针执行操作的最终目标.
从更大的角度来看,这一切都与所有权概念有关.某些操作依赖于拥有该项目,而其他操作可以通过借用该对象而逃脱(可能是可变的).引用(&foo)不授予所有权,它只是借用.
为什么使用
self而不是&self在函数的参数中有趣?
转移所有权通常是一个有用的概念 - 当我完成某些事情时,其他人可能拥有它.在Rust中,这是一种更高效的方法.我可以避免分配副本,给你一份副本,然后扔掉我的副本.所有权也是最宽容的国家; 如果我拥有一个物体,我可以随心所欲地使用它.
这是我创建的用于测试的代码:
struct IteratorOfStringReference<'a>(&'a String);
impl<'a> Iterator for IteratorOfStringReference<'a> {
    type Item = &'a String;
    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}
struct FileLikeThing {
    string: String,
}
impl FileLikeThing {
    fn iter(&self) -> IteratorOfStringReference {
        IteratorOfStringReference(&self.string)
    }
}
struct Dummy {
    xslg_file: FileLikeThing,
    buffer: String,
}
impl Dummy {
    fn dummy(&mut self) {
        for line in self.xslg_file.iter() {
            self.buffer.clear();
            for current_char in line.into_bytes().iter() {
                self.buffer.push(*current_char as char);
            }
            println!("{}", line);
        }
    }
}
fn main() {}
令人沮丧的是这个问题被标记为重复,因为这是 Rust 新手的死胡同的另一个例子。对于新手来说,很难从这些具体答案中概括出来,这意味着他们只会被下一个实例所困(参考:我)。
为了提供一些概括,以帮助解决一般的“无法摆脱”问题,首先考虑一下为什么会出现这个问题。主要原因有两个:
在第一种情况下,您无意中做了一些需要所有权的事情。那么解决方案就是找到一种不需要所有权的替代方案。这正是本问答中概述的情况。而不是into_bytes(),它需要所有权,因为它消耗了它的输入,替换为as_bytes()它将做同样的事情而不声明所有权。
解决方案不明显的原因是编译器假设您确实想要使用line,并有用地将其替换为*line. 因此,它会抛出误导性错误,通知您取消引用没有帮助。你对如何解决这个问题绞尽脑汁move,导致出现像创可贴这样的问题clone()。
事实上,您从一开始就不想取消引用,而只需要找到一个与借用引用一起使用的替代方案。
现在,在第二种情况下(这与我在顶部引用的“重复”问答中更相关),错误是相同的,但您无意中借用了而不是声明所有权。实现方法时通常会出现这种情况,因为方法通常使用对它的引用self而不是使用它。顺便说一句,这完全是可选的- 方法可以选择消耗而不是借用self,尽管我无法想象这非常实用,除非该方法返回一个Self返回值。
在第二种情况下,令人困惑的是,绝望clone()将再次出现以解决问题,至少在短期内如此。但考虑到根本原因,根本解决办法有点不同。由于您实际上确实想要声明所有权(也许您正在尝试删除数据或将其转移给另一个所有者),因此您需要明确这一点。
在简单的情况下,这可能只是&从您获取变量的地方删除 - 无论是您调用的函数的返回值,还是作为当前函数的参数。
但通常你无法做出这样的改变。相反,你必须剥夺所有权。但你不能让当前的主人空手而归——你需要给他们留下一些有效的东西。我知道执行此操作的唯一方法是使用 oftake或fromswap之一。到达 1.40是迄今为止最简单的 - 它只是为所有者提供价值,并为您提供当前价值的所有权。和替代方案为前任所有者提供了其他东西,如果该类型没有.replacestd::memtakeDefaultswapreplaceDefault
这是之前和之后的示例:
pub struct SimpleLinkedList<T> {
    head: Option<Box<Node<T>>>
}
pub struct Node<T> {
    value: T,
    next: Option<Box<Node<T>>>
}
impl<T> SimpleLinkedList<T> {
    pub fn pop(&mut self) -> Option<T> {
        if self.head.is_none() { return None; }
        let value = self.head.unwrap().value;
        self.head = self.head.unwrap().next;
        return Some(value);
    }
}
这会引发“无法移出可变self.head引用后面的位置”错误,因为unwrap()Consumers self。
有很多方法可以改进这一点,但出于说明目的,这就是必要的:
use std::mem;
pub struct SimpleLinkedList<T> {
    head: Option<Box<Node<T>>>
}
pub struct Node<T> {
    value: T,
    next: Option<Box<Node<T>>>
}
impl<T> SimpleLinkedList<T> {
    pub fn pop(&mut self) -> Option<T> {
        if self.is_empty() { return None; }
        let old_head = mem::take(&mut self.head);
        let old_head_unwrapped = old_head.unwrap();
        self.head = old_head_unwrapped.next;
        return Some(old_head_unwrapped.value);
    }
}
self.head无论如何,我们都会替换该值,因此通过调用take它,我们可以声明所有权。然后我们可以自由地将其传递给unwrap()并随后覆盖原始的self.head.
所以总而言之,为什么你会得到“无法移出借用的内容/参考”?要么是因为你只需要借钱,并且必须确保你所做的一切都不会消耗,要么是因为你实际上想要消费并且必须take拥有。