• Rust所有权(非常重要)


    1.其他语言的内存管理,在C/C++中,开发者需要手动释放申请的内存资源。在Java(jvm) 中,JVM采用了垃圾回收的机制,但是这样会降低运行时的效率,并非实时的,所以JVM会尽可能少的回收,这很矛盾,所以还是不好,而 Rust 对栈内存和堆内存一视同仁,超出作用域一律自动释放。Rust 的这个特点在兼顾性能的情况下、有效的减少了代码量和内存泄漏隐患。。

    2.Rust所有权规则:每一个值都有一个变量,称为其所有者,一次只能有一个所有者,当所有者不在程序运行范围内的时候,该值将会被删除。

    3.变量范围:rust以大括号为界,超出范围则会被回收。申请长度不确定的内存时,会使用到堆这种结构。

    4.变量与数据的交互方式:第一种是移动(move),第二种是克隆(clone)。

    移动(move): 多个变量可以在 Rust 中以不同的方式与相同的数据交互

    let x=3;    let y=x;
    
    • 1

    这里的数据是基本数据类型,所以不需要存到堆中,只在栈中复制和移动。
    基本数据类型:
    1.所有整数类型,例如 i32 、 u32 、 i64 等。
    2.布尔类型 bool,值为 true 或 false 。
    3.所有浮点类型,f32 和 f64。
    4.字符类型 char。
    5.仅包含以上类型数据的元组(Tuples)。
    如果发生的情况在堆里面,就会不一样:

    let s1=String::from("hello"); 
    let s2=s1;
    
    • 1
    • 2

    此处hello需要存在堆里面,后面释放的时候,并不会释放两次,因为s2=s1时,s1已经无效了,后面无法使用。
    在这里插入图片描述
    克隆(clone): 但如果需要将数据单纯的复制一份以供他用,可以使用数据的第二种交互方式。

    fn main() {
        let s1 = String::from("hello");
        let s2 = s1.clone();
        println!("s1 = {}, s2 = {}", s1, s2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里释放资源的时候,会被当成两个资源释放。

    5.函数的所有权机制

    fn main() {
        let s = String::from("hello");
        // s 被声明有效
    
        takes_ownership(s);
        // s 的值被当作参数传入函数
        // 所以可以当作 s 已经被移动,从这里开始已经无效
    
        let x = 5;
        // x 被声明有效
    
        makes_copy(x);
        // x 的值被当作参数传入函数
        // 但 x 是基本类型,依然有效
        // 在这里依然可以使用 x 却不能使用 s
    
    } // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放
    
    
    fn takes_ownership(some_string: String) {
        // 一个 String 参数 some_string 传入,有效
        println!("{}", some_string);
    } // 函数结束, 参数 some_string 在这里释放
    
    fn makes_copy(some_integer: i32) {
        // 一个 i32 参数 some_integer 传入,有效
        println!("{}", some_integer);
    } // 函数结束, 参数 some_integer 是基本类型, 无需释放
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    也就是说,如果函数变量当作参数传入函数,他和移动的效果是一样的。

    6.函数返回值的所有权机制

    fn main() {
        let s1 = gives_ownership();
        // gives_ownership 移动它的返回值到 s1
    
        let s2 = String::from("hello");
        // s2 被声明有效
    
        let s3 = takes_and_gives_back(s2);
        // s2 被当作参数移动, s3 获得返回值所有权
    } // s3 无效被释放, s2 被移动, s1 无效被释放.
    
    fn gives_ownership() -> String {
        let some_string = String::from("hello");
        // some_string 被声明有效
    
        return some_string;
        // some_string 被当作返回值移动出函数
    }
    
    fn takes_and_gives_back(a_string: String) -> String { 
        // a_string 被声明有效
    
        a_string  // a_string 被当作返回值移出函数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    被当作函数返回值的变量所有权将会被移动出函数,并返回到调用函数的地方,而不会被无效释放。

    7.引用(reference)和租借(borrow)、

    引用其实可以当成是指针,是变量的间接访问方式

    fn main(){
    let s1=String::from("hello");
    let s2=&s1;
    println!("s1 is {},s2 is {}",s1,s2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里的s1并不会被无效,s1个地址,它指向堆中的量,s1把地址给s2,s2指向s1。
    在这里插入图片描述
    记住这张图,s2=&s1。。当然在函数中传递引用参数,也是和这里一样的效果。
    引用不会获得值的所有权,引用只是租借值的所有权。在引用之后s2=&s1,如果s3=s1,也就是将s1移动到s3,这时s2就无法租借s1而失效,需要重新租借s3。
    同时,也不可以租借之后对值进行修改,这个和租房子一个道理,未经房东允许,不可以改变房子。
    但是,也可以房东允许你装修:

    fn main(){
    let mut s1=String::from("run");
    let s2=&mut s1;
    s2.push_str("oob");
    println!("{}",s2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里的&mut修饰的是可变的引用类型。
    提出一个很重要的概念:同时写内存造成数据不一致的问题。
    可变引用不允许多重引用,但是不可变引用可以。

    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s;
    println!("{}, {}", r1, r2);
    
    • 1
    • 2
    • 3
    • 4

    这段代码有问题,因为多重可变引用了s。而rust对于这种设计就是出于对并发状态下发生数据访问碰撞的考虑,防止同时写进行修改数据造成数据的不一致性。

    8.悬垂引用(dangling reference)

    定义:指的是那种没有实际指向一个真正能访问的数据的指针(注意,不一定是空指针,还有可能是已经释放的资源)。这个就是C语言里面说的“野指针”,任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
    在Rust语言里面不允许出现,如果有,编译器会发现它。

  • 相关阅读:
    [附源码]计算机毕业设计JAVA学习资源共享与在线学习系统
    宝塔环境PHP网站要配置HTTPS,SSL证书如何申请
    规则引擎(JVS-rules):从应用到场景的全面解析
    el-tabs(标签栏)的入门学习
    Ubuntu18.04安装mysql8.0,亲测有效(内附安装时会遇到的一些情况)
    黑马瑞吉外卖之删除分类
    好细的Vue安装与配置
    第一天-基本知识整理,目的:能写点东西
    Oracle检查点队列–实例崩溃恢复原理剖析
    学习 C++ 编程,怎么才能找到合适的练手项目?
  • 原文地址:https://blog.csdn.net/phthon1997/article/details/125527369