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

创建新对象≠值移出?我对Rust移动语义的理解有误吗?

问题解答

你没理解错Rust的Move语义,只是打印的地址不是你以为的对象底层数据地址——你打印的是「变量/结构体字段本身的内存地址」,而非String实际存储内容的堆内存地址。

核心误区拆解

  • &composers[0].name 取的是Vec堆内存中Person结构体里name字段的地址(Vec的元素默认存在堆上)。
  • &first_name 取的是main函数栈上局部变量first_name的地址。
    这两个属于不同内存区域的地址,自然不一样,但这和Move语义本身无关。

Move语义的本质

对于String这种非Copy类型,Move操作不会复制底层的堆数据,只是把栈上的元数据(String的指针、长度、容量三个字段)从原位置复制到新变量,同时标记原位置变量为无效(避免二次释放)。也就是说,原composers[0].name里的String和first_name里的String,指向的是同一块堆内存。

验证方法

修改代码打印String底层数据的地址,就能看到它们是同一个:

fn main() {
    struct Person {
        name: Option<String>,
        birth: i32,
    }
    let mut composers = Vec::new();
    composers.push(Person {
        name: Some("Palestrina".to_string()),
        birth: 1525,
    });

    // 打印原name中String的底层数据地址
    println!("first_elem string data addr {:p}", composers[0].name.as_ref().unwrap().as_ptr());
    let first_name = std::mem::replace(&mut composers[0].name, None);
    // 打印first_name中String的底层数据地址
    println!("first name string data addr {:p}", first_name.as_ref().unwrap().as_ptr());

    // 对比变量/字段地址(本来就不同)
    println!("first_elem addr {:p}", &composers[0].name);
    println!("first name addr {:p}", &first_name);
}

运行后会发现,前两行打印的底层数据地址完全相同,证明Move确实只是转移了所有权,没有创建新的对象。

关于std::mem::replace

这个函数的作用就是安全地从可变引用中移出值:它把目标位置的原值Move出来,同时放入你指定的新值(这里是None)。整个过程中,原值的所有权被转移到新变量,底层数据没有被复制。

内容的提问来源于stack exchange,提问作者P.Jo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 13:00:59