Rust 探索(五)—— 所有权(二)
Rust 探索(五)—— 所有权(二)
¶1. 所有权与函数
将值传递给函数其实在语义上类似于对变量进行赋值;因而,将变量传递给函数将会触发移动或者复制
1 | fn main() { |
其实可以将向函数传递参数简单地考虑为向函数的形参进行赋值
¶2. 返回值与作用域
函数可以返回值,而在此过程中会发生所有权的转移
1 | fn main() { |
返回值的所有权转移的规则与先前是类似的:将一个值赋值给另一个值时会转移所有权;当一个持有堆数据的变量离开作用域,它的数据会通过drop()
的方式被清理回收,除非发生转移
¶3. 引用与借用
有的时候,我们想要的不仅仅是返回值,还有原来的参数,那么可以用元组一次返回多个值,但这可能显得不是那么规范,但是没有问题,不是吗?
1 | fn main() { |
为了解决这样尴尬的情景,Rust提供了引用
实际上,在调用calculate_length()时,s1通过移动进入了函数的参数,由s取代,而后s返回,又移动给了外面接收的s2,但在此过程中,并没有修改其内容,却一直移动它,显然很没有必要
1 | fn main() { |
现在,函数没有多余的废话了,使用&
声明引用,允许在不获取所有权的前提下使用值
&
就像s拿着绳子,把传进来的s1和它绑一块
引用不持有值的所有权,也就是说他自己不能行动,因此当引用离开作用域,它指向的值不会被丢弃
1 | fn calculate_length(s: &String) -> usize { // 声明为指向s1的引用 |
通过引用传递参数给函数的方法称为借用,就像借东西一样,有借有还,但是默认还给别人的还是原来的样子(不可修改)
引用默认是不可变的,Rust不允许我们去修改引用指向的值
¶4. 可变引用
如果实在需要修改引用的内容,也是可以的,当然需要提前和借你东西的人打声招呼,即使用mut
声明可变引用
1 | fn main() { |
需要注意的修改一共有3处,首先是定义函数参数的时候,需要声明为&mut
,而后在传递参数的时候也需要显式表示为&mut
,同时这个参数本身还要是mut
的
对于特定作用域中的特定数据来说,一次只能声明一个可变引用
Rust通过规范约束避免了数据竞争问题
- 两个或两个以上的指针同时访问同一空间
- 其中至少有一个指针会向空间中写入数据
- 没有同步数据访问的机制
这些情形都可能发生不可预知的行为,从而导致复杂的bug,Rust通过代码规范的约束在编译的时候就阻止这种可能
我们无法同时创建不可变引用和可变引用,其实这很好理解,可变引用随意一改,不可变引用那里就发生了灵异事件,不是不可变吗,这不睁眼说瞎话呢嘛?
但是不可变引用可以有多个,因为它们只是读取内容,而不进行修改
¶5. 悬垂引用
我们有很多拥有指针概念的语言,比如C、C++,在使用它们的过程中很容易出现一种错误——悬垂指针
这类指针曾经指向某处内存,但是如今已经被释放或者重新分配,已经物是人非,其上的操作会非常危险
而在Rust语言中编译器可以确保引用永远不会进入这种悬垂状态
1 | fn main() { |
s在函数结束时离开作用域,那么指向的内存被释放,而返回的是该区域的引用,这便是悬垂,那么编译器识别并报错
1 | fn main() { |
¶6. 引用的规则
主要是进行一些总结和归纳:
- 在任何一段给定的时间里,只能拥有一个可变引用或者任意多个不可变引用
- 引用总是有效的