为什么 Rust 会抱怨借用以及如何修复?完整解析

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:当 Rust “不理解” 你的时刻

\n

你在用 Rust 编程。一切顺利。编译器保持沉默。你感觉自己像内存的掌控者。然后——砰!——它出现了:

\n
error[E0507]: cannot move out of borrowed content\n  --> src/main.rs:10:9\n   |\n10 |         let value = self.data;\n   |                     ^^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait\n
\n

或者它的近亲:

\n
error[E0507]: cannot move out of `*self` which is behind a shared reference\n
\n

如果你是新手,这看起来像是一个魔法诅咒。如果你有经验,你知道这只是来自 Borrow Checker(借用检查器)的又一课。

\n

在本文中,我们将解析这些错误的含义,为什么 Rust 如此严格,以及——最重要的是——如何通过实际代码示例来修复它们。这不是枯燥的理论,而是一份在所有权和借用世界中生存的实用指南。

\n\n

要点简述:所有权、借用和“移动”

\n

在深入细节之前,让我们回顾一下 Rust 的三大支柱:

\n
    \n
  • 所有权 (Ownership): Rust 中的每个值都只有一个所有者。
  • \n
  • 借用 (Borrowing): 你可以通过引用(&T — 不可变,&mut T — 可变)“借用”一个值,而不获取所有权。
  • \n
  • 移动 (Move): 当你将一个值赋给一个新变量或传递给一个不带 & 的函数时,所有权被移动。原始变量变为无效。
  • \n
\n

当你试图移动一个你只有引用 (&T) 的值时,就会出现 cannot move out of borrowed content 错误。Rust 禁止这样做,因为移动后引用将变成“悬垂指针”(dangling pointer)。而正如你所知,Rust 不会容忍这种事情。

\n\n

场景 1:尝试通过 &self 移动结构体的字段

\n

最常见的情况。你有一个结构体,并且想从一个接受 &self 的方法中返回它的一个字段。

\n\n

有错误的代码

\n
struct User {\n    name: String,\n    age: u32,\n}\n\nimpl User {\n    // 试图“永久”交出 name\n    fn give_name(&self) -> String {\n        self.name // 错误!\n    }\n}\n\nfn main() {\n    let user = User { name: "Alice".to_string(), age: 30 };\n    let name = user.give_name();\n    println!("{}", name);\n}\n
\n

编译器会说:

\n
error[E0507]: cannot move out of `self.name` which is behind a shared reference\n
\n

为什么? self 是 &User。我们不拥有 self,我们只是借用了它。试图移动 self.name(类型为 String,未实现 Copy)意味着在方法调用后,所有者(user)将失去它的字段。但引用 &self 仍然存在!Rust 保护我们免于灾难。

\n\n

如何修复?

\n

根据你的任务,有三种主要方法。

\n\n

方法 1:克隆(最简单,但并非总是高效)

\n

如果你确实需要值而不是引用,并且愿意为复制付出代价:

\n
fn give_name(&self) -> String {\n    self.name.clone() // 可行!创建一个副本。\n}\n
\n

缺点: 对于 String,clone() 会在堆上创建数据的完整副本。对于大字符串来说成本很高。

\n\n

方法 2:返回引用(修改签名)

\n

如果调用代码只需要一个字段的引用:

\n
fn give_name(&self) -> &String {\n    &self.name // 返回引用,没有移动\n}\n
\n

现在我们不是移动,而是借用。一切公平。调用代码将获得

博客

书籍推荐