Rule No.1
Ownership背后全都围绕这下面这一唯一规则:
Every value has a single owner that determines its lifetime. 每个值只有一个拥有者可以决定它的生命周期.
也就是说: Owner销毁(dropped), 值也销毁. 整个程序的数据像一个Ownership Tree🌲
Copy & Move
非Copy types 在赋值、传参和返回时移动(Move)其所有权, 原变量不可引用.
Copy type判断标准:
所有在销毁(dropped)时需要做些特殊处理操作(释放堆上内存或其它类型资源)的类型, 均为不可拷贝.
例如:
String
Box<T>
File
文件类型Mutex<T>
简单数字类型都实现了Copy Trait, 赋值拷贝时直接在栈上生成一份拷贝.
1 | let str1 = "somnambulance".to_string(); |
对于有指向heap数据的数据类型(包括自定义类型)不允许实现自定义Copy trait.
如果要允许移除(Move)一些被索引的内容, 例如
Vec<String>
可以用Option<T>,
被Move后, 原先的值为None.
1 | let s = vec!["udon".to_string(), "ramen".to_string(), "soba".to_string()]; |
执行上面第一语句后, 内存布局如下:
赋值给t
后, 内存布局如下:
再使用s
赋值就报错了, s已经是uninitialized状态.
Box<T>
简单的指向堆栈上数据的指针:
let b = Box:new(int);
赋值Move原指针所有权
可以用于递归类型或在编译时期无法计算对象大小的场景
1
2
3
4
5{
let point = Box::new((0.625, 0.5)); // point allocated here
let label = format!("{:?}", point); // label allocated here
assert_eq!(label, "(0.625, 0.5)");
} // both dropped here
内存布局如下:
Rc and Arc: Shared Ownership
有时候很难满足数据只有一个Owner, 因此引入共享所有权, 允许多个引用.
变量在栈上实际只是一个指针, 指向堆上一个维护引用计数及存放实际数据类型的内存.
⚠️这里并没有违反前面的第一条原则, 因为决定实际数据生命周期的Owner是这个Rc/Arc本身.当最后一个引用dropped, 整个数据释放.
共享的引用都是
immutable
, 不可修改共享数据.循环引用会造成死锁, 这是Rust可能内存泄露的一点. 要避免可以使用
std::rc::Weak
1 | let s: Rc<String> = Rc::new("shirataki".to_string()); |
clone()
后内存布局如下:
二者差异(仅此而已):
Rc<T>
非线程安全计数引用Arc<T>
线程安全的计数引用, short for Atomic Reference Count
All images are copyrighted by original authors Jim Blandy & Jason Orendorff who wrote in the book Programming Rust.