(done) 速通 rustlings(12) 所有权

向量 vector 的 "所有权"

如下代码,第25行会在编译阶段报错:

rust 复制代码
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
    let mut vec = vec;

    vec.push(88);

    vec
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    // TODO: Make both vectors `vec0` and `vec1` accessible at the same time to
    // fix the compiler error in the test.
    #[test]
    fn move_semantics2() {
        let vec0 = vec![22, 44, 66];

        let vec1 = fill_vec(vec0);

        assert_eq!(vec0, [22, 44, 66]);
        assert_eq!(vec1, [22, 44, 66, 88]);
    }
}

原因是:在 Rust 中,Vec 是一个拥有堆上数据的类型。

当你把它当作参数传给 fill_vec(vec0) 时,所有权从 vec0 转移到函数的形参里:

复制代码
fn fill_vec(vec: Vec<i32>) -> Vec<i32> { ... }

let vec1 = fill_vec(vec0);  // vec0 的所有权被 move 给函数形参了
// ^^^^^^^^          ^^^^^^^
//      ↑               ↑
//     变量           函数参数

移动发生后,原来的变量 vec0 不再拥有数据,所以编译器禁止再使用它;

这正是为了避免两个 Vec 同时尝试释放同一块内存(双重释放)的不安全情况。

因此下面的 assert_eq!(vec0, ...) 会报错------vec0 已经不再有效。

常见解决办法:

  • 传 vec0.clone() 给函数,保留一个副本;
  • 或者让函数借用:fn fill_vec(vec: &mut Vec) { ... }。
    这两种方式都避免了移动,从而让 vec0 在后续仍可使用。(我没试过)

如25行改为:

rust 复制代码
let vec1 = fill_vec(vec0.clone());

不可变矢量通过函数形参转可变矢量

给 fill_vec 的形参开头加上 mut 关键字

rust 复制代码
fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
    vec.push(88);

    vec
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn move_semantics3() {
        let vec0 = vec![22, 44, 66];
        let vec1 = fill_vec(vec0);
        // vec0 此时不能再用,所有权已经交给 vec1
        assert_eq!(vec1, [22, 44, 66, 88]);
    }
}

矢量的别名

每个变量只能拥有一个别名,如果要用其它别名取代,需要确保程序后续没有再使用原来的别名(更好的理解:所有权同一时间只能给一个变量):

rust 复制代码
fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    // TODO: Fix the compiler errors only by reordering the lines in the test.
    // Don't add, change or remove any line.
    #[test]
    fn move_semantics4() {
        let mut x = Vec::new();
        let y = &mut x;
        // `y` used here.
        y.push(42);
        // The mutable reference `y` is not used anymore,
        // therefore a new reference can be created.
        let z = &mut x;
        z.push(13);
        assert_eq!(x, [42, 13]);
    }
}

函数获取变量所有权

通常来说,如果一个函数对某个变量是只读的,就不需要获取该变量数据的所有权。

若这个函数需要改变某个变量,那么需要获取该变量数据的所有权。(但听说也有 borrow mutably 方案,没试过)

rust 复制代码
#![allow(clippy::ptr_arg)]

// Borrows instead of taking ownership.
// It is recommended to use `&str` instead of `&String` here. But this is
// enough for now because we didn't handle strings yet.
fn get_char(data: &String) -> char {
    data.chars().last().unwrap()
}

// Takes ownership instead of borrowing.
fn string_uppercase(mut data: String) {
    data = data.to_uppercase();

    println!("{data}");
}

fn main() {
    let data = "Rust is great!".to_string();

    get_char(&data);

    string_uppercase(data);
}

可变借用 vs 取得所有权

&mut T 和 mut T 在 Rust 里代表两种不同的 "使用权利":

  • 🔧 可变借用(&mut T)

    是什么:一个可变的引用,函数只借用数据,自己不拥有。

    特点:

    • 只能在借用期间访问,出了作用域就没了。
    • 借用期间,调用方不能同时再使用这个值(编译器保证独占访问)。
    • 函数结束后,原值还在,调用方继续拥有它。

    适用场景:你想 修改调用者的数据,但不想拿走它,比如往 Vec 里 push。

  • 🔄 取得所有权(mut T)

    是什么:把数据的所有权从调用方移动到函数里,函数现在完全控制它。

    特点:

    • 函数可自由变更、移动甚至丢弃这个值。
    • 调用方在调用之后就不能再使用这个变量了(除非函数把它返回)。
    • 没有借用期限制,值属于函数直到它被返回或移动到别处。

    适用场景:你要 消费这个值、转交给其他地方或返回一个新值给调用者。

📝 总结

&mut T 是一次 临时的独占借用,原主人保留所有权。

mut T 是一次 所有权转移,函数获得永久控制权。

选择哪一个,取决于调用方之后是否还需要使用这个值。


相关推荐
doiito6 小时前
【Agent Harness】Gliding Horse 设计细节 -- 不跟风开发自己的AI Agent
架构·rust·agent
doiito8 小时前
【Agent Harness】Gliding Horse 核心设计理念,不跟风开发自己的AI Agent
ai·rust·架构设计·系统设计·ai agent
花褪残红青杏小18 小时前
Rust图像处理第6节- 均值模糊 & 中值模糊:3×3 邻域的两种经典玩法
rust·webassembly·图形学
子兮曰1 天前
前端工具链的「Rust 化」:一场没有赢家的军备竞赛?
前端·后端·rust
星栈1 天前
写 Dioxus Demo 不难,难的是把它写成项目
前端·rust·前端框架
mCell1 天前
【锐评】桌面端技术营销:别拿跑分当工程判断
前端·rust·electron
武子康1 天前
调查研究-201 Rust 里的 dev build 和 release build:为什么同一份代码性能差这么多?
后端·架构·rust
doiito1 天前
【Agent Harness】Gliding Horse 的 L2 作战地图:让多 Agent 协作从“摸黑”变成“透明”
ai·rust·架构设计·系统设计·ai agent
星栈2 天前
我用 Rust + Dioxus 做了个全栈跨平台笔记应用:再把新建、编辑和交付补上
前端·rust·前端框架
独孤留白2 天前
从C到Rust:基本类型 C 的隐式不确定 vs Rust 的显式确定
rust