Rust 语言从入门到实战
唐刚
Rust 语言中文社区联合创始人
5266 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 36 讲
Rust 语言从入门到实战
15
15
1.0x
00:00/00:00
登录|注册

02|所有权(上):Rust是如何管理程序中的资源的?

你好,我是 Mike。今天我们来讲讲 Rust 语言设计的出发点——所有权,它也是 Rust 的精髓所在。
在第一节课中,我们了解了 Rust 语言里的值有两大类:一类是固定内存长度(简称固定尺寸)的值,比如 i32、u32、由固定尺寸的类型组成的结构体等;另一类是不固定内存长度(简称非固定尺寸)的值,比如字符串 String。这两种值的本质特征完全不一样。而怎么处理这两种值的差异,往往是语言设计的差异性所在
就拿数字类型来说,C、C++、Java 这些语言就明确定义了数字类型会占用内存中的几个字节,比如 8 位,也就是一个字节,16 位,也就是两个字节。而 JavaScript 这种语言,就完全屏蔽了底层的细节,统一用一个 Number 表示数字。Python 则给出了 int 整数、float 浮点、complex 复数三种数字类型。
Rust 语言因为在设计时就定位为一门通用的编程语言(对标 C++),它的应用范围很广,从最底层的嵌入式开发、OS 开发,到最上层的 Web 应用开发,它都要兼顾。所以它的数字类型不可避免地就得暴露出具体的字节数,于是就有了 i8、i16、i32、i64 等类型。
前面我们说到,一种类型如果具有固定尺寸,那么它就能够在编译期做更多的分析。实际上固定尺寸类型也可以用来管理非固定尺寸类型。具体来说,Rust 中的非固定尺寸类型就是靠指针或引用来指向,而指针或引用本身就是一种固定尺寸的类型。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入讲解了Rust语言中的所有权概念以及栈与堆的管理。首先介绍了Rust中的值分为固定尺寸和非固定尺寸两类,以及不同编程语言对数字类型的处理差异。随后,详细解释了栈和堆的概念,以及它们在程序中的作用和区别。文章还提到了栈帧与堆空间资源的关系,以及内存泄漏问题。最重要的是,文章详细阐述了Rust中的所有权概念,包括所有权的基础规则和作用域,以及通过具体例子展示了在Rust中变量赋值时的所有权转移特性。通过这些内容的讲解,读者可以更好地理解Rust语言中的资源管理和内存管理机制。文章的深入讲解有助于读者更全面地理解Rust语言的特性和设计理念。文章还提到了Rust中的所有权概念,包括所有权的基础规则和作用域,以及通过具体例子展示了在Rust中变量赋值时的所有权转移特性。通过这些内容的讲解,读者可以更好地理解Rust语言中的资源管理和内存管理机制。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Rust 语言从入门到实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(41)

  • 最新
  • 精选
  • 二夕
    思考题 1: 无法通过编译,可以将第 5 行代码修改为:let tmp_s = s.clone(); 思考题 2: 由于 Point 没有实现 Copy trait,所以在赋值过程中会产生 Move。

    作者回复: 真棒!

    2023-10-23归属地:浙江
    2
    22
  • Geek_147053
    看完了刚更新的前2篇,感觉挺适合新手的,虽然长,但看下来一点儿也不枯燥,讲的挺有意思

    编辑回复: 加油!一直跟下去,不会让你失望的

    2023-10-24归属地:北京
    6
  • 小云同学
    好奇一个问题: fn main() { let s1 = "hello world".to_string(); let s2 = s1; // s1 不再有效,它的值发生了移动 } 显然上面代码很好理解,如果换一种方式。 fn main() { let arr = ["hello world".to_string()]; let s = arr[0]; // 报错:String 没有实现 Copy trait } 这里报错了,按理说不是应该会将所有权从 arr[0] 转移到 s 上面吗?但是 Rust 却提示 cannot move。我的理解是这样的,因为像数组这样的结构如果有效,那么它内部的每一个成员必须都要有效。如果数组中的某个元素发生了移动,那么会导致整个数组不可用,于是为了避免这种情况,Rust 要求数组里面的元素必须是可 Copy 的。如果需要转移所有权,那么 Rust 编译器就报错。否则会给用户造成一个错觉,好端端的数组为啥就不能用了。 如果用这个理论来解释的话,那么就又产生了一个问题。 fn main() { // 元素 let arr = ("hello world".to_string(), "hello world".to_string()); let s = arr.0; // 此处不报错 // arr.1 可以正常打印 println!("{}", arr.1); // hello world // 但打印 arr.0 和打印 arr 则报错,提示发生了移动 } 所以我很好奇,为啥会产生这种情况。为什么元组(还有结构体)允许局部的元素发生移动,但数组却不可以呢?还请老师帮忙解答一下

    作者回复: 思考得非常棒。我的理解是结构体,元组这种属于异质复合结构,每个元素都有单独的类型指定。array,Vec属于同质连续结构。可能语言设计者认为后者比前者更紧致,随意置某个位置无效的话会引起更多麻烦,比如sort操作咋办?迭代操作咋办? 估计是有这方面的考虑从而禁止把所有权从数组和动态数组中移动出来。

    2023-11-01归属地:北京
    2
    5
  • duwoodly
    1. String类型,实际数据在堆上存储。 let tmp_s = s 循环第一次的时候,会移动所有权,s在栈上的内存虽然还在,但是被编译器视为无效变量或无效状态,所以第二次及以后的循环就不能实验变量s了,编译器会报错。 2. 结构体类型默认没有实现Copy trait, 赋值过程也会移动所有权。 当然从底层看,这个Point结构体的成员都是基本类型(基本类型实现了Copy), 所以这个结构体的值是保存在栈上的,所以赋值操作,实际上底层是在栈上完整拷贝了一次Point结构体,但是编译器依然会把原来的Point结构体变量视为无效状态或无效变量。

    作者回复: 👍很棒

    2023-10-24归属地:重庆
    5
  • Forest
    思考题 1:编译报错;两处错误:变量 i 没有使用和 s 的所有权; 修改后如下: fn main() { let s = "I am a superman.".to_string(); for _ in 1..10 { let tmp_s = s.clone(); println!("s is {}", tmp_s); } } 思考题 2:移动;如果结构体中包含实现了 Copy trait,则会进行复制而不是移动

    作者回复: 对的。

    2023-10-25归属地:四川
    2
    3
  • Geek_582a5d
    目前看来感觉看着最舒服的rust相关系列文章了,催催更新。这个系列会持续跟进,感谢作者。

    编辑回复: 如果觉得有帮助的话,欢迎转发给身边的朋友哦,你一票我一票 Mike 老师要出道!

    2023-10-24归属地:上海
    3
  • 水不要鱼
    老师,关于移动还是复制的那段话,我有个迷惑。。我能不能理解其实都是复制了栈上的数据,比如 a = 10u32 b = a 由于 10u32 是放栈上的,实际上是把 a 的数据复制了一份,然后 b 绑定了这份数据,因为数据是独立的,所以所有权也是独立的,a 和 b 各自拥有各自数据的所有权。 而 String 也是一样,把 a 的数据复制了一份到 b 上,但是这时候 a 的数据实际上是堆上数据的地址,所以复制的数据是这个堆上数据的地址,而不是堆上的数据,所以实际数据只有一份,所有权也是一份,这时候 b = a 就会把这一份所有权同时交给 b

    作者回复: 你描述得很棒。所有权的背后意义是 资源管理。谁能掌控资源的管理谁就有所有权,然后是有所有权的变量要负责资源的释放。你理解到这一点就云开雾散了。

    2023-11-03归属地:广东
    2
  • Test
    默认做复制所有权的操作: 裸指针类型 裸指针类型也是copy语义吧

    作者回复: 默认做复制所有权的操作: 裸指针类型。 这句话从哪里来的?文章里面没有。

    2023-10-25归属地:北京
    3
    2
  • 咖啡☕️
    问题 1 改成 ``` let tmp_s = &s; ``` 即可

    作者回复: 👍

    2023-10-24归属地:上海
    2
  • 刘永臣
    问题1:本来以为坑在s 因为s在第一次遍历时已经转移了,所以第二次遍历肯定会出错,所以编译器会报错,没有问题;但是实际上循环条件中i 会被多次修改,所以声明i时也应该增加mut; 问题2:结构体数据复合结构,所以对其进行赋值是实际上进行了所有权的转移。

    作者回复: 👍

    2023-10-24归属地:北京
    2
收起评论
显示
设置
留言
41
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部