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

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


相关推荐
铅笔侠_小龙虾15 小时前
rust 学习(9):结构体
开发语言·学习·rust
铅笔侠_小龙虾18 小时前
rust 学习(12):常用集合(上)-- Vec
rust
铅笔侠_小龙虾20 小时前
rust 学习(10):枚举与模式匹配
rust
落知秋1 天前
RUST中的trait是什么?
笔记·rust
Embedded-Xin2 天前
中间件—zenoh零基础入门
linux·中间件·rust·机器人·自动驾驶·嵌入式
程序员爱钓鱼2 天前
Rust 可变借用详解:&mut T 与安全修改数据
后端·面试·rust
SomeB1oody2 天前
【RustyML入门】2.9. MeanShift
开发语言·后端·机器学习·rust·教程
SomeB1oody3 天前
【RustyML入门】2.10. 主成分分析
开发语言·后端·机器学习·rust·教程
yushikong3 天前
关于rust开发ch32x033f8p6的一些记录
开发语言·后端·rust