• Rust所有权机制


    前言

    所有权机制是Rust的核心功能之一,对于初学者算是一个重难点,它保证了程序无需GC,即可自动化的释放内存
    希望这篇博客可以帮助你快速理解所有权机制

    概念

    Rust的值都只有一个变量是该值的所有者
    所有者可以切换,但是值在任一时刻有且仅有一个所有者
    当所有者离开作用域,该值将被丢弃

    错误示例

    在变量的作用域之外访问该变量

    fn main() {
        {
            let s = String::from("hello"); // 创建字符串,该字符串的所有者是变量s
        } // 离开作用域,该字符串被丢弃
        println!("{}", s); // 编译报错,此时变量s已被释放,无法访问变量s
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    访问一个所有权被转移的变量

    fn main() {
    	let s = String::from("hello"); // 创建字符串,该字符串的所有者是变量s
        let a = s; // 字符串的所有权从变量s传递给变量a
    	println!("{}",s); // 编译报错,此时字符串的所有权已被转移,无法在通过变量s访问字符串
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    访问一个所有权被转移的变量

    fn main() {
    	let s = String::from("hello"); // 创建字符串,该字符串的所有者是变量s
        takes_ownership(s); // 字符串的所有权从变量s传递给函数takes_ownership
    	println!("{}",s); // 编译报错,此时字符串的所有权已被转移,无法在通过变量s访问字符串
    }
    
    fn takes_ownership(s: String) {
    	println!("我拿到了字符串\"{}\"的所有权", s); // 拿到字符串的所有权
    } // 离开作用域,该字符串被丢弃
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    借用

    在上述错误示例中,我们不难发现,如果我们把String字符串的所有权传递给函数,那么等到函数结束,我们就无法在使用该字符串了,对于我们,这显然是无法接受的,那么几种解决方法解决这个问题,例如

    fn main() {
        let mut s = String::from("hello");
        s = takes_ownership(s);
        println!("{}", s);
    }
    
    fn takes_ownership(s: String) -> String {
    	println!("我拿到了字符串\"{}\"的所有权", s); // 拿到字符串的所有权
        s // 返回该字符串
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    fn main() {
        let mut s = String::from("hello");
        s = takes_ownership(s);
        println!("{}", s);
    }
    
    fn takes_ownership(s: String) -> String {
    	println!("我拿到了字符串\"{}\"的所有权", s); // 拿到字符串的所有权
        s // 返回该字符串
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    但是上述做法并不优雅,Rust推荐的做法是我们通过借用来解决这个问题,借用就是创建一个引用的行为,例如

    fn main() {
    	let s = String::from("hello");
        takes_ownership(&s); // 将字符串的引用传递给函数
        println!("{}", s);
    }
    
    fn takes_ownership(s: &String) { // 借用到字符串的引用
    	println!("我借用到了字符串\"{}\"的引用", s);
    } // 此时字符串的所有权并没传递给函数,应此该函数没有资格释放该字符串
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果我们需要在函数内部修改字符串,那么我们可以借用字符串的可变引用

    fn main() {
    	let mut s = String::from("hello");
        takes_ownership(&mut s); // 将字符串的引用传递给函数
        println!("{}", s);
    }
    
    fn takes_ownership(s: &mut String) { // 借用到字符串的可变引用
    	println!("我借用到了字符串\"{}\"的可变引用", s);
    	s.push_str(" world"); // 修改该字符串
    } // 此时字符串的所有权并没传递给函数,应此该函数没有资格释放该字符串
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可变引用虽好,但是有个注意事项,就是同一时刻可变引用只能有一个,当存在可变引用的时候,就不能有不可变引用,但是当可变引用不存在的时候,就可以存在数个不可变引用,例如

    fn main() {
    	let mut s = String::from("hello");
        let (a, b) = (&s, &s); // 借用两个字符串的不可变引用
        println!("不可变借用a:{}, 不可变借用b:{}", a, b);
        let c = &mut s; // 借用字符串的可变引用,此时前面的两个不可变引用不再生效
        c.push_str(" world");
        println!("{}", c);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    还有一个注意事项就是,引用的内容必须合法,例如下面的错误示范

    fn main() {
        let s = dangle();
    }
    
    fn dangle() -> &String {
        let s = String::from("hello"); // 创建字符串,该字符串的所有者是变量s
        &s // 返回字符串的引用,但是此时字符串要已经离开作用域被释放了,因此当函数结束,这个引用将是一个非法的悬垂引用,不过还好,在rust中这种行为无法通过编译
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    引用规则总结

    1. 在任意时刻只能有一个可变引用,要么只能有多个不可变引用
    2. 引用必须有效
  • 相关阅读:
    多尺度残差超分辨率
    mysql部分特性
    MySQL 定时计划任务 事件的使用
    windows配置skywalking集群
    设计模式之美——单一职责原则和开闭原则
    spark相关网站
    yolov5调用zed相机实现三维社交距离检测(单类别)
    抖音短视频怎么做——今抖云创
    uni-app支持vue3(小程序、h5、app等)多端开发
    9.2 【MySQL】独立表空间结构
  • 原文地址:https://blog.csdn.net/qq_67733273/article/details/133939009