盒子与标签:所有权入门

原理

所有权系统是 Rust 替代 GC 的核心机制,基于三个编译期规则实现静态内存管理:

栈上的值按位复制(memcpy)成本低;堆上的值通过指针访问,赋值时仅拷贝指针(Move),不深拷贝堆数据。编译器在函数退出点自动插入 drop 调用释放堆内存。

Copy trait 标记按位复制安全(stack-only 类型)。非 Copy 类型在赋值后原变量被编译器视为”未初始化”,产生 use-after-move 编译错误。这是通过 MIR(Mid-level IR)阶段的 move-checker 实现的 non-lexical lifetime 分析。

根本原因:避免 double-free。两个变量拥有同一块堆内存的所有权,作用域结束时均执行 drop → 两次 free → 未定义行为。

C: 内存管理与指针


语法

Move 语义

let s1 = String::from("hello");
let s2 = s1;            // s1 所有权转移给 s2
// println!("{}", s1);  // 编译错误:use of moved value

Copy 类型(自动复制)

let x = 5;
let y = x;              // x 仍然有效(i32 是 Copy)
println!("{} {}", x, y);

实现 Copy 的基本类型:

类型
i8, i16, i32, i64, i128, isize
u8, u16, u32, u64, u128, usize
f32, f64
bool, char
&T(不可变引用)
(T1, T2, ...) 当所有字段都是 Copy

Clone — 显式深拷贝

let s1 = String::from("hello");
let s2 = s1.clone();    // 堆数据深拷贝,s1 仍有效

函数调用中的所有权

fn consume(s: String) { } // 接收所有权
 
fn main() {
    let s = String::from("hi");
    consume(s);
    // println!("{}", s); // 编译错误:s 已移动
}
fn give_back(s: String) -> String { s } // 归还所有权
 
let s = give_back(String::from("hi")); // 用遮蔽接收

三条核心规则

  1. 每个值有且仅有一个所有者
  2. 所有者离开作用域时,值被 drop
  3. 赋值/传参时:Copy 类型自动复制,非 Copy 类型产生 move

元组与 Copy

let t1 = (1, 2, 3);             // 全 Copy → 元组可 Copy
let t2 = (1, String::from("h")); // 含非 Copy → 元组不可 Copy

实践

力扣问题

力扣: 力扣简单模拟题 — 涉及值传递与所有权

fn main() {
    let mut k: i64 = read_one();
    let mut day = 1;
    let mut sum = 0;
    while k > 0 {
        for _ in 0..day {
            sum += day;
            k -= 1;
            if k == 0 { break; }
        }
        day += 1;
    }
    println!("{}", sum);
}

AI 自检

  1. String 赋值时为何是 move 而非 copy?从内存布局解释(16或24字节栈数据 vs 堆数据)。
  2. 若元组 (i32, String) 不是 Copy,能否通过 clone 变通?clone 的代价是什么?