"cannot move out borrowed content" 从结构字段分配变量时

"cannot move out borrowed content" when assigning a variable from a struct field

我正在学习 Rust,并且正在与借用检查器作斗争。

我有一个基本的 Point 结构。我有一个 scale 函数可以修改点的所有坐标。我想从另一个名为 convert:

的方法调用此方法
struct AngleUnit;

struct Point {
    x: f32,
    y: f32,
    z: f32,
    unit: AngleUnit,
}

fn factor(_from: AngleUnit, _to: AngleUnit) -> f32 {
    1.0
}

impl Point {
    pub fn new(x: f32, y: f32, z: f32, unit: AngleUnit) -> Point {
        Point { x, y, z, unit }
    }

    fn scale(&mut self, factor: f32) {
        self.x *= factor;
        self.y *= factor;
        self.z *= factor;
    }

    fn convert(&mut self, unit: AngleUnit) {
        let point_unit = self.unit;
        self.scale(factor(point_unit, unit));
    }
}

现在出现以下错误:

cannot move out of borrowed content

我做错了什么?

您正在 self.unit 移动:

let point_unit = self.unit;

尝试使用引用或创建类型 Copy

完整 错误消息指出:

error[E0507]: cannot move out of borrowed content
  --> src/lib.rs:26:26
   |
26 |         let point_unit = self.unit;
   |                          ^^^^^^^^^
   |                          |
   |                          cannot move out of borrowed content
   |                          help: consider borrowing here: `&self.unit`

这有助于了解移动发生的位置。

最简单 解决方案是为您的 AngleUnit 类型实施 CopyClone。如果它是 Copy,您的代码将工作 as-is。如果只有 Clone,则必须显式调用 .clone() 进行复制。

如果你的类型无法制作Copy,那么你可以使用引用,正如编译器建议的那样:

fn factor(_from: &AngleUnit, _to: &AngleUnit) -> f32 {
    1.0
}
fn convert(&mut self, unit: AngleUnit) {
    let point_unit = &self.unit;
    self.scale(factor(point_unit, &unit));
}

原来的问题都归结为这一行:

let point_unit = self.unit;

point_unit 的值应该是多少?

如果我们值从self.unit移动到point_unit,那么self.unit的值会是多少? "easy" 解决方案是它是未定义的内存,但经验表明我们程序员会搞砸并引入 exciting-to-debug 问题。

我们可以自动复制该值,但是如果 AngleUnit 是一种占用 space 的 10 MiB 的类型,会发生什么?然后,一条看似无辜的台词吸走了一堆记忆和时间。这也不是很好。

相反,Rust 使类型默认 移动 并且你不能让对象处于未定义状态。某些类型可以选择自动复制的能力——这就是 Copy 特性。您还可以允许显式复制类型——Clone 特性。您还可以获得对现有值的 reference 并将其传递。借用检查器将在该引用不再有效后阻止您使用该引用。

你需要用 #[derive(Copy, Clone)] 属性标记 AngleUnit 以允许 Rust copy AngleUnit 从一个变量到另一个,就像你在 let point_unit = self.unit.

中所做的那样

默认情况下,Rust 只允许移动 你的结构,因为移动 通常比复制更有效。例如,如果您的结构中有一个 Vec 或一个 String,那么要 复制 程序必须为该结构分配新内存 VecString 堆上的内容。而 移动 一个 Vec 或一个 String 不会产生额外的堆分配。

在这种情况下,我猜 AngleUnit 只是一个小原始值的包装器,所以完全可以 复制 它。

这里是an example of your code with the attribute added

#[derive(Copy, Clone)]
struct AngleUnit;

struct Point {
    x: f32,
    y: f32,
    z: f32,
    unit: AngleUnit,
}

fn factor(from: AngleUnit, to: AngleUnit) -> f32 {
    1f32
}

impl Point {
    pub fn new(x: f32, y: f32, z: f32, unit: AngleUnit) -> Point {
        Point { x, y, z, unit }
    }

    fn scale(&mut self, factor: f32) {
        self.x *= factor;
        self.y *= factor;
        self.z *= factor;
    }

    fn convert(&mut self, unit: AngleUnit) {
        let point_unit = self.unit;
        self.scale(factor(point_unit, unit));
    }
}

fn main() {}