今天我们来讲讲rust最难,也是最重要的概念:
Ownership,Borrowing,Lifetimes
首先我们来看看:ownership(所有权)
我们来看看下面的代码:
let a = [1, 2, 3];
let b = a;
println!("{:?} {:?}", a, b); // [1, 2, 3] [1, 2, 3]
let a = vec![1, 2, 3];
let b = a;
println!("{:?} {:?}", a, b); // Error; use of moved value: a
我们把这两段代码,放在vscode里去build.
第一段代码没有问题,第二段代码报错。
vscode会报错信息:
println!("{:?} {:?}", a, b); // Error; use of moved value: `a`
| ^ value borrowed here after move
编译器已经告诉我们原因:value borrowed here after move
也就是说a对原来数据的所有权已经move(移动)给b,这时再访问a的值。编译器就会报告你犯了一个严重错误。
我们用简单的图示来说明:
如图,a原来有本书,现在给了b.
那现在,这本书就在b手上了。b拥有这本书。
对应代码就是:
let a = vec![1, 2, 3];
let b = a;// a把数据给b.
那这个时候,a已经没有对数据的所有权,就访问不了数据,编译器就报错。
那为什么第一段代码,又不报错呢?
原来,rust中分两种数据类型:
1.基本数据类型: 如:bool(布尔),char(字符),integer(整数),floating(浮点),arrays(数组),tuples(元组),slice(切片),字符串(str),函数指针(functions)
对基本类型的详细说明 可以参考英文教程:
https://learning-rust.github.io/docs/a6.variable_bindings,constants_and_statics.html
或中文参考:http://wiki.jikexueyuan.com/project/rust/primitive-types.html
2.非基本类型:
即除基本类型外的其它类型,一般为引用类型。
那rust对这两种类型会分别不同处理:
对基本类型,rust会对原来a的数据复制,并把复制数据赋值给b,并把原始数据的所有权状态 设置为“已复制( copied )”状态。
对非基本类型,rust会把原来a的数据移动,并把原始数据赋值给b,并把原始数据的所有权状态 设置为“已移动( moved )”状态。
针对这两种处理模式,rust内部又定义为两种类型:复制类型(Copy type ),移动类型( Move type )
但这里还要注意一点,对于函数指针类型,一般情况下为移动类型( Move type ),但如果它实现以下接口:
则它也是复制类型,执行复制的模式。
以上,就是所有权的基本概念和要点。
其实,我们重新思考多线程中的数据安全,rust的这种设计,是非常合理的。
(关于线程安全的问题,我之前写过java的高并发与锁的原理系列,见:https://www.cnblogs.com/gyc567/p/11014782.html)
首先,rust定义数据初始化动作为绑定,并且默认为不可变,如下:
let a =9;
a=10;//error
第二行代码 a=10;是会报错的。为什么?因为它是不可变的,如果要改变,就要把它定义为mut(可变),如下:
let mut a =9;
a=10;//correct
所以,rust的设计哲学就是默认所有线程操作都是不安全的。那所有数据默认为不可变,在多线程环境下,就是相当于所有线程都可以共享数据,是线程安全的,因为数据是不变的。
那回到上面的所有权的概念,复制类型(Copy type )就相当于数据共享,可以共享复制数据,但禁止写。移动类型( Move type )就相当于独占锁,线程拥有这把“锁”,才能访问数据,否则报错。
以上,希望对你有用。
如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust