Rust 通过单一所有权来限制任意引用的行为
Why
通常的编程语言为了管理内存 通常会采用三种方案:
- GC。在运行时不断扫描堆内存,看值是否还存在引用,如果不存在则释放内存。会造成性能的消耗
- 引用计数。在编译时自动添加维护引用计数的代码,减轻开发者维护堆内存的负担。但同样地,它也会有不小的运行时性能损耗
- 手动管理内存(C++)。通过业内的一些最佳实践来进行内存的管理,但是很鸡肋
以上方案都是通过管理引用的角度去处理的,但是它仍然解决不了根本问题—变量被无限制、有意无意的引用,导致难以管理。
那么如果我们从引用本身出发去处理,对引用本身做一些限制,不让开发者随意去引用呢?
那么我们应该加一些什么限制才可以做到清晰有效的引用呢?
What
所有权其实就是对堆内存上的引用进行有效管理的一种手段。
所有权决定了值的生杀大权,避免了堆内存上变量的多重引用问题
所有权规则:
- 一个值只能被一个变量拥有,这个变量被称为所有者
- 一个值同一时刻只能有一个所有者,也就是说同一时刻不能有两个变量拥有同一个值
- 当所有者离开作用域时,其拥有的值会被抛弃,内存得到释放
Move语义
Move
语义:赋值或者传参会导致值 Move
,所有权被转移,一旦所有权转移,之前的变量就不能访问
fn main() {
let data = vec![1, 2, 3, 4];
// 这里所有者已经转移,data内存释放,后续无法访问
let data1 = data;
// 调用sum函数时,data1的所有权也已经转移给了参数,data1后续也无法访问
println!("sum of data1: {}", sum(data1));
// 所有权已经转移所以报错
println!("data1: {:?}", data1); // error1
println!("sum of data: {}", sum(data)); // error2
}
fn sum(data: Vec) -> u32 {
data.iter().fold(0, |acc, x| acc + x)
}
那么如果我们在赋值或者传参数时,不想让所有权转移呢?也就是说我们仍然想访问原有的变量呢?
- 使用Clone方法,将数据克隆出一份副本
let data1 = data.clone();
- 如果如果你不希望值的所有权被转移,在 Move 语义外,Rust 提供了 Copy 语义。如果一个数据结构实现了 Copy trait,那么它就会使用 Copy 语义。这样,在你赋值或者传参时,值会自动按位拷贝(浅拷贝)。
- 使用借用(&)。
let data1 = &data;
当你在赋值或者传参的时候,默认会进行Move
,但是如果该值的类型实现了Copy Trait
,那么就会将值按位拷贝,而不是进行Move
。
实现了Copy Trait的类型
fn is_copy<T: Copy>() {}
fn types_impl_copy_trait() {
is_copy::();
is_copy::();
// all iXX and uXX, usize/isize, fXX implement Copy trait
is_copy::<i8>();
is_copy::<u64>();
is_copy::<i64>();
is_copy::<usize>();
// function (actually a pointer) is Copy
is_copy::<fn()>();
// raw pointer is Copy
is_copy::<*const String>();
is_copy::<*mut String>();
// immutable reference is Copy
is_copy::<&[Vec<u8>]>();
is_copy::<&String>();
// array/tuple with values which is Copy is Copy
is_copy::<[u8; 4]>();
is_copy::<(&str, &str)>();
}
fn types_not_impl_copy_trait() {
// unsized or dynamic sized type is not Copy
is_copy::();
is_copy::<[u8]>();
is_copy::<Vec>();
is_copy::();
// mutable reference is not Copy
is_copy::<&mut String>();
// array / tuple with values that not Copy is not Copy
is_copy::<[Vec<u8>; 4]>();
is_copy::<(String, u32)>();
}
fn main() {
types_impl_copy_trait();
types_not_impl_copy_trait();
}