1. 所有权规则
- Rust中的每一个值都有一个所有者变量
- 值在任一时刻有且只有一个所有者(变量)
- 当所有者离开作用域的时候,这个值将会被丢弃(drop) 这些规则在之后的举例说明里面都会有所体现,需要牢记
例1
let a = "hello";
{
let b = "aaa";
println!("{}",b);
}//执行到此时"aaa"这个值的所有者离开了作用域,"aaa"将会被丢弃
2.1 变量域数据交互的方式(一)——移动
例2.1.1
let mut s1 = String::from("hello");
s1.push_str(",move");
println!("{}", s1);
let s2 = s1;//在运行了这个语句之后,Rust认为s1不再有效
//此时使用s1将不能运行
//println!("{}",s1);//error
println!("{}",s2);
在例2.1代码运行到let s2 = s1的时候,我们拷贝了s1在栈上的数据(指针,长度和容量),但是并没有复制s1在堆上的数据,内存中数据的表现如图 2-1 所示
在这个过程中,s1存储在堆上的数据并没有被复制。而在完成了let s2 = s1语句的时候,s1将会被无效化,此时避免了s1,s2离开作用域时可能引发的二次释放的错误,因为s1被无效化,它并不会因为离开作用域而触发丢弃值的动作,只有s2在离开作用域的时候会丢弃它所拥有的"hello"的值。我们可以解读为s1被移动到了s2,只有s2是有效的,在移动的过程中,始终满足着Rust的所有权规则。
let s2 = s1之后s1被无效,满足了规则2,值在任一时刻都有且只有一个所有者
2.2 变量域数据交互的方式(二):克隆
在有些情况下,我们不但需要复制变量在栈中的数据,也需要复制其在堆中的数据,也就是深拷贝,这时就需要克隆操作,这时我们可以使用clone的通用方法,克隆在内存中数据的表现如图 2-2 所示
例2.2.1
let s1 = String::from("hello,clone");
let s2 = s1.clone();
println!("{}",s1);//此时sa仍能正常使用
println!("{}",s2);//仍然满足所有权规则
需要注意的是,如果变量是整型这样的数据,则以下代码是有效的
例2.2.2
let inta = 5;
let intb = inta;
println!("in stack:inta = {}, intb = {}",inta,intb);
上面这段代码没有调用clone函数,但是inta依然有效(没有被移动到intb上),原因是像inta,intb这样已知大小的类型(i32)会被整个存储到栈上,拷贝这个值的过程是非常快速的,可以直接使用拷贝。
在Rust上有一个叫做Copy trait的特殊注解,可以用在类似整型这样存储在栈上的类型上,如果一个类型实现了Copy trait,那么一个旧的变量在将其赋值给其他变量后仍然可用。(类似clone)。如果一个类型自身或者它的任何一部分实现了Drop trait,那么不允许其再实现Copy trait。
实现了
Copy的类型
- 所有的整数类型
- 所有的浮点数类型
- 布尔类型
- 字符类型
- 仅包含以上四种类型的元组
所有权与函数
将值传递给函数在语义上与给变量赋值类似,method(x)对x的影响类似于y = x
let s = String::from("hello");
takes_ownership(s);
//同复制一样,值的所有权从s交给了takes_ownership函数的some_string形参
//发生了移动,在执行了函数之后,some_string变量离开作用域,丢弃了它拥有的值
// println!("{}",s);//error此时发生了移动,s被弃用
//takes_ownership函数的定义
fn takes_ownership(some_string:String){
println!("{}",some_string);
}
返回值也可以转移所有权