有的语言完全使用手动内存管理,程序员必须负责向分配器归还它们申请的空间,也就是说,必须要有一个free
操作对应着一个allocate
操作。另一些语言使用自动内存管理系统(俗称GC),它们完全接管内存的创建以及回收(在不需要的时候)。
- 手动内存管理对人类不友好,人很难保证不出错,而且在项目中的各个模块的互相调用中很难确定一个对象的内存该由谁来管理
- 自动内存管理中的GC总是需要占用程序一部分运行时间和内存来做垃圾收集,并且垃圾回收并不是即时的。
Rust使用第三种内存管理方式,即——所有权系统。
所有权规则
- Rust中的每个值都有一个所有者(owner)
- 值在任意一刻有且只有一个所有者
- 当所有者(变量)离开作用域,这个值将会被丢弃
上面所说的是,值具有所有者,所谓所有者就是当前承载该值的变量,一定要搞清值和所有者的概念和区别。
变量作用域
{ // 进入作用域,目前s不可用
let s = String::from("HHH"); // 创建s,目前s可用
// s可用
} // 离开作用域,s不可用
这和其它语言的变量作用域没什么差别,不过在rust中,当变量s
离开作用域,rust会为我们自动调用它的drop
方法,String
的实现者可以实现该方法来完成内存的释放。在rust中,内存在拥有它的变量离开作用域后就被自动释放。
貌似不实现也会有drop方法
所有权移动
let str1 = String::from("hello");
let str2 = str1;
println!("{}", str1);
在大部分语言中,上面的代码都是将str1
的引用赋值给str2
,并不会造成堆上数据(字符串”hello”)的拷贝。在rust中也是这样的,不过,rust的所有权规则有如下定义:
值在任意一刻有且只有一个所有者
所以,实际上,上面的代码实际上有一个错误,在第1行中,str1
持有这个字符串值,在第2行中,这个字符串值的所有权转移到了str2
上,所以,str1
无效了。你尝试编译时会出现这样的错误,编译器告诉你值已经被转移了:
考虑如果rust允许str1
和str2
同时持有该值,那么在作用域结束后,它会对这一个值drop两次。
rust的各种限制可以将所有(至少是大部分吧)内存错误在编译时被发现
下面的代码都不会出错,因为它们操作的都是字面量,和其它语言一样,字面量在赋值时是直接复制值的,而不是传递引用。这涉及到copy trait
,稍后会说。
let str1 = "hello";
let str2 = str1;
println!("{}", str1);
let x = 1;
let y = x;
println!("{}", x);
克隆和拷贝
当你确实想对某一个对象进行值赋值时,而非引用赋值,即类似其它语言中拷贝的概念,你可以使用通用方法clone
来实现克隆:
let str1 = String::from("hello");
let str2 = str1.clone();
println!("{}", str1);
如上面所说,那些只在栈上存储的数据,比如i32
、字符串字面量,对它们的传递本身就是使用拷贝的,如果一个类实现了copy trait
,它们在进行赋值给其它变量时就会使用拷贝,也不会发生所有权移动,因为此时它们已经是两个值了。
rust中实现copy trait
的类型:
- 所有整数类型,比如 u32。
- 布尔类型,bool,它的值是 true 和 false。
- 所有浮点数类型,比如 f64。
- 字符类型,char。
- 元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。
所有权与函数
注意,在调用函数时实际上也是做了一次隐含的赋值操作,这里也涉及到所有权移动。
fn take_ownership(some_string: String) { // 值的所有权移动到`some_string`变量上
println!("{}", some_string);
} // 超出作用域,销毁`some_string`的值,drop被调用
fn main() {
let s = String::from("Hello");
take_ownership(s); // 变量s的值发生所有权转移,它不属于s了
println!("{}", s); // move occurs because `s` has type `String`, which does not implement the `Copy` trait
} // s不具有任何值的所有权,所以什么都不会发生
相同的,对于实现了copy trait
的类型,赋值时进行一次拷贝,所以不影响拷贝前的值
fn main() {
let x = 15;
makes_copy(x); // 发生值拷贝
println!("{}", x); // correct because x implement copy trait
} // x被移除作用域,同时它是栈上值,直接无了
fn makes_copy(some_integer: i32) { // 值被拷贝了一份新的给some_integer
println!("{}", some_integer);
} // some_integer被移除作用域,同时它是栈上值,直接无了
未完…
原文地址:http://www.cnblogs.com/lilpig/p/16888542.html