一、 所有权系统的主要规则
Rust所有权系统的主要规则有:
- Rust中每一个值都有一个变量关联,这个变量称作owner
- 一个值某一时刻只能有一个owner
- 当owner的作用域结束,对应值的生命周期也结束,会即时被销毁
二、 Move 与 Clone
对于不同变量而言,所有权对相同内存地址的值存在两种不同的交互方式,Move 与 Clone。
1. Move
以字符串为例:
let s1 = String::from("hello");
let s2 = s1;
从 s1 到 s2,并不会拷贝 s1 中的内容,然后新赋值给 s2 ,因为这样会导致开辟内存空间时花费大量时间。字符串中的内容存储在堆上,然后使用一个指针指向堆中的地址。
再而言,同样也不会复制一个 s1 的指针地址给 s2 ,因为如果 s1 与 s2 有着相同的地址空间,那么在变量离开作用域时,他们会调用 Drop 函数,释放地址空间,但是因为他们有着相同的地址,会导致二次释放问题。两次释放会导致内存损坏,可能会导致安全漏洞。
为了保证内存安全,在 let s2 = s1 的赋值完成后,Rust 认为 s1 已经是无效的。因此 Rust 在 s1 离开作用域后,不再需要释放操作(drop)。可以知道,后续对 s1 的访问都会是非法的。这是区别于大多数变成语言的。
这类似移动了 s1 的地址指针给 s2 的操作就称为 Move 。
2. Clone
对于类似深拷贝的操作,Rust 提供了 clone 方法来实现,如下:
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
3. Copy Trait
在实际使用过程中,可能会发现像下面这种类似的现象:
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
虽然没有调用 clone 方法,但是所有权并没有转移。看起来像是执行了复制一样。
Rust 这样做的原因是这两个整型都完全存储在栈上,所以我们完全可以快速复制其值,没有必要让其中一个变量失效。
Rust 中有一个Copy trait的特殊注解,可以用在类似整型这样的存储在栈上的类型上。
如果一个类型实现了 Copy trait ,那么一个变量在赋值给另一个同类型变量后,将仍然有效。
但是如果一个类型实现了Drop trait,则Rust不允许用Copy trait来注解一个类型,这将产生编译错误。即 Drop trait 与 Copy trait 不可共存。
那么哪些类型实现了 Copy trait 呢?你可以查看给定类型的文档来确认,不过作为一个通用的规则,任何一组简单标量值的组合都可以实现 Copy,任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型:
- 所有整数类型,比如 u32。
- 布尔类型,bool,它的值是 true 和 false。
- 所有浮点数类型,比如 f64。
- 字符类型,char。
- 元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。
三、 引用与借用
引用(reference)像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。 与指针不同,引用确保指向某个特定类型的有效值。
下面是如何定义并使用一个(新的)calculate_length 函数,它以一个对象的引用作为参数而不是获取值的所有权:
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
&s1 语法让我们创建一个指向值 s1 的引用,但是并不拥有它。因为并不拥有这个值,所以当引用停止使用时,它所指向的值也不会被丢弃。
我们将创建一个引用的行为称为借用(borrowing)。正如现实生活中,如果一个人拥有某样东西,你可以从他那里借来。当你使用完毕,必须还回去。我们并不拥有它。
但是对于普通的引用,我们不能修改其地址的值。
可变引用
在 & 的基础上加一个 mut,例子如下:
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}