【第五课】Rust所有权系统(一)

目录

前言

所有权机制的核心

再谈变量绑定

主人变更-所有权转移

总结


前言

这节课我们来介绍下rust中最重要的一个点:所有权系统。这是网上经常说rust无gc的秘密所在。在开始之前,我们来想想JVM系语言,在做垃圾回收的过程,1.怎么找到垃圾,或者说怎么判断该内存上放的是垃圾。2.什么时候清理垃圾。3.怎么清除垃圾。JVM上不同的垃圾回收器,不同的垃圾回收算法都是围绕着这三个问题展开。我们假设一个场景:中学班级毕业,所有毕业的同学需要写完毕业手册后才能离开教室,离开教室后可以清理该同学的东西了。怎么做呢?当同学离开教室的时候,这个同学的东西就可以被清理了。什么叫做离开教室呢?我们都知道代码是有作用域的,说的简单就是那对{},当某个变量离开作用域后,这个变量如果是某块内存的所有者,那么这块内存就可以被释放了,是不是很有趣的思路,其实在c/c++中,内存的释放是程序员自己写的,rust中的内存释放是靠所有权系统保证的,在编译阶段rust编译器会根据所有权系统的规则对代码进行检查,保证内存都能正确的申请和释放,如果不满足规则,编译就不允许通过,这也是很多人说rust不能一次编译,但是一旦编译通过,就是安全可靠的程序。这节课我们来看看rust中所有权系统吧,提前预告涉及的概念多且陌生,耐心看完。

听起来rust中的内存回收是不是挺简单的,只要同学离开教室就可以清理该同学的东西了,假设某本字典是A同学和B同学一起买的,当A同学离开的时候,把字典清理了,这时候B同学想查字典就会造成空指针,这就是悬垂引用,当B同学离开教室时,想要再扔一次字典,此时字典已经被扔过一次,这就会造成内存二次释放。这2个问题都是比较严重的问题。

涉及概念

所有权系统ownership system

所有权ownership

借用borrowing

生命周期lifetimes

所有权机制的核心

1.每一块内存空间都有一个所有者,该所有者对于该内存有读写权限和释放内存的权限,我们将这个所有者称为主人

2.每一块内存空间在任意时刻都只有一个主人,不能同时存在2个主人,比如上面例子中的字典,字典只能有一个主人,但是A同学可以把字典给同学B,这样主人就从A转移给B,其他同学可以借字典使用,但是B才是主人,哪些不是主人的同学没有释放内存的权限,所以离开教室时不会丢字典

3.当主人离开作用域时,该主人释放他的内存区域

再谈变量绑定

再一次看看变量绑定

rust 复制代码
fn main() {
    let x = "hello".to_string();
}

知道为什么在其他语言中这样的代码叫做变量x的声明和初始化,但是在rust中这叫做变量绑定了吧,将字符串绑定到变量x上,从此之后,x就是hello所在内存的主人了,当x走出了作用域之后,hello所在的内存就要被清理。

主人变更-所有权转移

我们说过主人是可以变更的,那么怎么变更呢,通过赋值,下面的代码hello的主人第一次是x,在y = x后,hello的主人已经是y了,这种行为叫做移动move,也叫做所有权转移,但是这里要注意一下,有些值是移动,有些值是复制,比如一些基本类型,例如数字,当操作y=x时,是复制了一个值出来,假设x=7,当操作y=x时,此时内存中有2个7,他们的主人分别是x和y,但是像字符串这样的,才是移动。

重要的3句话:

1.rust中有2种语义:复制语义和移动语义

2.复制语义不会导致所有权转移,移动语义会导致所有权变更

3.实现了Copy trait的都是复制语义,没有实现Copy trait的都是移动语义

所以很简单了,根据上面的三句话,基本类型实现了Copy,所有权不会转移,String没有实现Copy,所以会转移,对于元组,如果元组中的所有值都实现了Copy,那么元组也实现了Copy,不会转移。我贴了一个例子在后面。

rust 复制代码
fn main() {
    let x = "hello".to_string();
    {
        let y = x;
    }
}

看看这个元组的所有权例子,t1的所有元组都实现了Copy,所以t1也实现了Copy,于是在t2 = t1时,会将这个元组复制一份,t1 和 t2分别是2块不同内存的主人,因为是主人,所以我们可以访问t1的元素。t3由于存在一个String类型的元素,String是移动语义,所以t3也是移动语义,那么在t4=t3时,该元组的内存已经属于了t4,不再是t3的了,所有权发生了转移,那么当我们继续使用t3.1时,会收到报错,下面贴了报错的图,可以看到rust编译器解释的很清楚,t3因为没有实现Copy,所以它被移动了,移动了之后,我们就不能使用t3去读取元组了,因为t3不是主人了,不是主人就没有读写权限了。

rust 复制代码
fn main() {

    // 元组的所有元素都实现了copy,元组就实现了copy
    let t1 = (1, 2, true);
    let t2 = t1; // 所有权不会转移
    println!("t1.0 = {}", t1.0);

    // 元组存在元素没有实现copy,元组没有实现copy
    let t3 = (String::from("hello"), 1, 2, 3);
    let t4 = t3; // 这里所有权转移给t4, t3不再是主人
    println!("t3.1 = {}", t3.1);
}

那么结构体呢?结构体会发生所有权转移么?我们来试一试,实践出真知。

代码如下,其实对于结构体来说,不管结构体中的元素有没有实现copy,结构体默认都是移动语义,所有权会发生移动。但是大家会问,这不合理啊,如果结构体中的元素都是copy语义,结构体其实也可以是copy语义啊,是的,没错。所以说结构体默认是移动语义,如果结构体的元素都是copy语义,可以在结构体顶部加上#[derive(Copy,Clone)]后,结构体就是copy语义了,但是如果结构体中存在移动语义的元素,即使加了这个,结构依然是移动语义,关于这个derive属性,可以百度了解一下哦。

rust 复制代码
fn main() {

    let s1 = Student1{
        age: 23,
    };

    let s2 = s1;
    println!("s1.age = {}", s1.age);

    let s3 = Student2 {
        name: String::from("hello"),
    };

    let s4 = s3;
    println!("s3.name = {}", s3.name);

}

struct Student1 {
    age: i32,
}

struct Student2 {
    name: String,
}

总结

这节课是rust无gc的核心秘密所在,也是和其他编程语言非常不同的地方,rust的所有权保证了每一块内存都有自己唯一的主人,当主人离开作用域后,内存可以释放,从而rust的性能可以堪比c/c++这些需要手动申请释放内存的语言,rust程序需要的内存量比起java来说也少了很多倍,原因就在这里了。这一节课值得好好研究,也可以在网上看些其他材料综合理解,同时也欢迎大家评论交流哈。

相关推荐
lpfasd1232 小时前
鸿蒙OS与Rust整合开发流程
华为·rust·harmonyos
m0_480502641 天前
Rust 登堂 之 类型转换(三)
开发语言·后端·rust
ftpeak1 天前
Rust Web开发指南 第六章(动态网页模板技术-MiniJinja速成教程)
开发语言·前端·后端·rust·web
编码浪子1 天前
趣味学Rust基础篇(数据类型)
开发语言·后端·rust
编码浪子1 天前
趣味学习Rust基础篇(用Rust做一个猜数字游戏)
学习·rust
love530love2 天前
怎么更新 cargo.exe ?(Rust 工具链)
人工智能·windows·python·rust·r语言
Source.Liu2 天前
【typenum】 23 倒序存储的无符号整数(private.rs片段)
rust
咸甜适中2 天前
rust语言(1.88.0)sqlite数据库rusqlite库(0.37.0)学习笔记
数据库·rust·sqlite·rusqlite
jinlei20092 天前
在python 代码中调用rust 源码库操作步骤
开发语言·python·rust
m0_480502643 天前
Rust 登堂 之 函数式编程(三)
开发语言·后端·rust