(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 是一次 所有权转移,函数获得永久控制权。

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


相关推荐
shimly1234564 小时前
(done) 速通 rustlings(7) 全局变量/常量
rust
敲敲了个代码4 小时前
构建工具的第三次革命:从 Rollup 到 Rust Bundler,我是如何设计 robuild 的
开发语言·前端·javascript·后端·rust
lpfasd1234 小时前
Tauri 中实现自更新(Auto Update)
rust·tauri·update
shimly1234564 小时前
(done) 速通 rustlings(10) 基本数据类型
rust
shimly1234565 小时前
(done) 速通 rustlings(8) 函数
rust
busideyang5 小时前
MATLAB vs Rust在嵌入式领域的角色定位
开发语言·matlab·rust
Source.Liu6 小时前
【a11】项目命名笔记:`a11` (合一)
rust·egui
Source.Liu20 小时前
【egui】官方示例 hello_world 完全解析
rust·egui
CS生1 天前
Rust环境准备
开发语言·后端·rust