You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust借用结构体的所有权问题:实例与引用的差异

Rust结构体实例与共享引用的所有权区别解析

我希望理解结构体实例(如rect1: Rectangle)与“借用指针”类型实例(如rect2: &Rectangle)之间的区别。我理解在into_area中使用原始self会导致所有权从main()转移到该方法,正试图深入理解这种所有权转移机制。

示例代码如下:

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn into_area(self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1: Rectangle = Rectangle {
        width: 90,
        height: 10,
    };

    let rect2: &Rectangle = &Rectangle {
        width: 15,
        height: 10,
    };

    let area1 = rect1.into_area();

    println!("rect1 area1: {}", area1);

    // E0382
    let _temp1 = rect1;

    // E0507
    let area2 = rect2.into_area();

    println!("rect2 area2: {}", area2);

    // 编译器无报错
    let _temp2 = rect2;
}

编译时触发的E0507错误如下:

error[E0507]: cannot move out of `*rect2` which is behind a shared reference
  --> src/main.rs:31:17
   |
31 |     let area2 = rect2.into_area();
   |                 ^^^^^^-----------
   |                 |     |
   |                 |     `*rect2` moved due to this method call
   |                 move occurs because `*rect2` has type `Rectangle`, which does not implement the `Copy` trait
   |
note: `Rectangle::into_area` takes ownership of the receiver `self`, which moves `*rect2`
  --> src/main.rs:7:18
   |
7  |     fn into_area(self) -> u32 {
   |                  ^^^^

核心区别与错误原因解析

1. 结构体实例与共享引用的本质差异

  • 结构体实例(Rectangle)是拥有所有权的值:rect1完全持有对应的Rectangle实例,调用into_area(self)时,会将整个实例的所有权转移给方法。方法执行完毕后,该实例会被销毁,因此后续再使用rect1会触发E0382(使用已移动的值),这符合所有权转移的基本逻辑。
  • 共享引用(&Rectangle)是借用指针:rect2本身是一个指针变量,main拥有这个指针的所有权,但指针指向的Rectangle实例是被借用的。Rust的共享引用遵循“只读、允许多个借用”的规则,绝对禁止从共享引用后方移动原始值——因为一旦移动,其他可能存在的共享引用就会变成悬垂引用,直接违反内存安全原则。这就是E0507错误的根源,和编译器是否能推断引用数量无关,是Rust的核心安全规则直接禁止了这种操作。

2. 为什么rect2无法转移所有权?

当你调用rect2.into_area()时,Rust会自动对引用进行解引用(即尝试访问*rect2),试图将被引用的Rectangle实例的所有权转移给into_area方法。但由于rect2是共享引用,Rust不允许这种操作——如果允许,就会破坏共享引用的语义,导致其他潜在的引用访问已被销毁的内存。

3. “重新获取所有权”的具体操作

在当前场景中,rect2指向的是一个临时创建的匿名Rectangle实例,这个实例的所有权没有被绑定到任何变量上,因此你无法直接“重新获取”它的所有权。要实现类似逻辑,正确的做法是:

  • 先将临时实例绑定到一个拥有所有权的变量上:
    let rect_instance = Rectangle { width: 15, height: 10 };
    let rect2 = &rect_instance;
    
  • 此时如果需要转移所有权,直接使用拥有所有权的rect_instance调用into_area即可,而不是通过引用rect2。
  • 如果你一定要基于引用获取所有权(比如引用来自其他地方),可以在结构体实现Clone trait后,调用rect2.clone()创建一个新的实例,将新实例的所有权转移给方法,这样原引用仍然有效:
    #[derive(Clone)]
    struct Rectangle {
        width: u32,
        height: u32,
    }
    
    // 调用方式
    let area2 = rect2.clone().into_area();
    

内容的提问来源于stack exchange,提问作者Preston Maness

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:44:57