1、所有权的概念:
程序需要管理自己在运行时使用的计算机内部空间。Rust语言采用包含特定规则的所有权系统来管理内存,这套规则允许编译器在编译的过程中执行检查工作,而不会产生任何的运行时开销。
(1)、所有权规则:
- Rust中的每一个值都有一个对应的变量作为它的所有者;
- 在同一时间内,值有且仅有一个所有者;
- 当所有者离开自己的作用域时,它持有的值就会被释放掉。
(2)、变量的作用域:
作用域是指一个对象在程序中的有效范围。
变量的有效范围说明:
rust
{//S未被声明,在这里还不可用
let s = "hello";//从这里开始S变得可用
//执行与S相关的操作
}//作用域到这儿结束,变量S再次不可用
2、String类型:
String类型的数据存放在堆上,是一个可变的字符串类型。Rust在变量结束的地方会自动调用drop函数,String类型的作者在drop函数中编写了自动释放内存的程序。
注意:任何简单的标量的组合类型都是可以Copy的,任何需要分配内存或某种资源的类型都不会是Copy的。下面是一些拥有Copy这种trait的类型:
- 所有的整数类型;
- 仅拥有两种值的布尔类型;
- 字符类型;
- 所有的浮点类型;
- 如果元组所包含的所有字段的类型都是Copy的,那么这个元组也是Copy的。
3、所有权与函数:
将值传递给函数在语义上类似于对变量进行赋值。将变量传递给函数将会触发移动或复制,就像赋值语句一样。
rust
fn main()
{
let s = String::from("hello");//变量s进入作用域
takes_owership(s);//s的值被移动进了函数
//s的值在这里不再有效
let x = 5;//x进入作用域
make_copy(x);//x的值被传递进了函数
//由于i32是Copy的,所以在这儿依旧可以使用x
}//x的值首先离开作用域,随后是s
//由于s已经发生了移动,所以不会发生什么情况
fn takes_owership(some_string:String){//s进入作用域
println!("{}",some_string);
}//some_string在这里离开作用域,drop函数被自动调用
//some_string所占用的内存被释放
fn make_copy(some_integer:i32){//some_integer进入作用域
println!("{}",some_integer);
}//some_integer在这里离开作用域,没有什么发生
4、返回值与作用域:
函数在返回值的过程中也会发生所有权的转移。
变量所有权的转移总会遵循相同的模式:将一个变量赋值给另一个变量时就会转移所有权。当一个持有堆数据的变量离开作用域时,它的数据就会被drop清理回收,除非这些数据的所有权转移到了另一个变量上。
5、引用与借用:
引用是指在不获取所有权的前提下使用某个变量的值。
示例:
rust
fn main(){
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}'is {}.",s1,len);
}
fn calculate_length -> usize{
s.len()
}
在调用calculate_length函数时使用了&s1
作为参数,并且在该函数的定义中,使用&String替代了String。
其中&
代表的就是引用的语义。
由于引用并不持有值的所有权,所以当引用离开当前作用域的时候,它指向的值也不会被丢弃,同样也不能对引用值进行更改。
这种通过引用传递参数给函数的方法也被称为借用。
6、可变引用:
将变量声明为mut s
,再使用&mut s
给函数传递一个可变引用,并将函数签名改为some_string:&mut String
来使其接收一个可变引用作为参数。
可变引用在使用上有一个限制:对于特定作用域中的特定数值来说,一次只能声明一个可变引用。以下示例违背了这条规则:
rust
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
Rust中的这一限定性规则可以帮助在编译时避免数据竞争。数据竞争与竞态条件十分相似,它会在满足以下三种情况时发生:
- 两个或两个以上的指针同时访问同一空间;
- 其中至少有一个指针会向空间中写入数据;
- 没有同步数据访问的机制。
数据竞争会导致未定义行为,这些未定义行为会使得出现的bug难以被修复和诊断。
7、悬垂引用:
悬垂指针指向曾经存在的某处内存地址,但该内存已近被释放甚至是被重新分配另作他用了。
8、切片:
切片允许我们引用集合中某一段连续的元素序列,而不是整个集合。
(1)、字符串切片:
字符串切片是指向String对象中某个连续部分的引用,它使用方式如下:
rust
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
使用方括号中指定切片的范围区间[starting_index..ending_index]
,其中starting_index
是切片起始位置的索引值,ending_index
是切片终止位置的下一个索引值。切片数据结构在内部存储了指向起始位置的引用和一个描述切片长度的字段,这个描述切片长度的字段等价于ending_index
减去starting_index
。
注:当范围是从第一个元素开始时,可以省略两个点号之前的值;当切片包含String中的最后一个字节时,可以省略双点号之后的值。
(2)、其它类型的切片:
字符串切片是专门用来处理字符串的,除此之外,Rust还存在其他更为通用的切片类型,以数组为例:
rust
let a = [1,2,3,4,5];
let slice = &a[1..3];
这与字符串切片的工作机制完全一样。