作用域规则

主要是针对所有权、借用、生命周期规范

RAII

  • Rust中强制进行RAII资源获取即初始化 ,在任何对象离开作用域的时候会调用它的析构函数,因此不需要手动释放内存,避免了资源泄露
  • 析构函数通过Drop 提供,自定义的类型需要自己实现drop trait,其余的自动实现了这种trait。

所有权和移动

  • 因为变量要进习释放所用有的资源,因此只有一个所有者,因此就有了引用 这种方法。
  • 指针指向堆地址,如果进行移动,这个时候之前的指针会失去作用,新的指针会拥有原来堆上的内存原来指针会失效 ,手动执行释放后,新的指针也不能访问。
  • 而对于变量的移动,让两个变量拥有相同的值,两个变量是都可以访问的。

可变性

  • 可变指针可以进行修改指针指向的内容,但是不可变指针不行。所有权 转移:由不可变指针->可变指针,会得到这样的结果。

部分移动

  • 在对结构体进行解构的过程中,可以使用ref关键字进行绑定,绑定过ref的参数属于是引用的参数。而没有标注的则是move模式。

借用

  • 借用&T的存在巧妙解决了所有权T和使用中的问题。
  • 编译器通过借用检查,保证了引用总是指向有效的对象,

存在引用指向对象的过程中,该对象不能够进行销毁

可变性

  • 借用有两种形式:&mut T(可以允许读、写数据),&T只能够读不能够写数据。
  • 不能够可变的借用不可变对象,如果需要,先创建一个可变拷贝。

别名使用

数据允许多次不可变借用,但是有不可变借用的同时,不能使用可变借用
同一时间只允许有一次可变借用,当最后一次可变借用使用过后(通常是使用过println!之后),才可以再次借用

ref模式

  • ref相当于&,不同点是ref在左边,&在右边,而在参数中基本相同。

生命周期

  • 生命周期和作用域是不同的概念。
  • 生命周期在结束的时候必须要保证它的引用已经结束,这部分由编译器检查。

显式标注

  • 生命周期函数在使用的过程中:
1
2
foo<'a, 'b>
// `foo` 带有生命周期参数 `'a` 和 `'b`
  • 像是这种生命周期参数,则foo不能超出a、b中间任意一个生命周期
  • 同样的,如果函数体内部有局部变量,这种局部变量的生命周期小于函数体,则不可将短生命周期的局部变量转换为长生命周期。

函数

  • 生命周期有省略的情况,在这种情况之外:
  • 任何引用都必须要有标注好的生命周期
  • 任何被返回的引用要有和它输入量相同或者是静态类型的生命周期
  • 引用的生命周期要大于函数体本身,不然没法返回(类似于上一句,但是一定要大于函数体本身,不然会产生悬空指针,是指引用输入的变量,而在形参上和函数体保持一致)
  • 不能将局部变量返回,因为它的生命周期短于函数体本身:
1
2
3
4
5
fn invalid_output<'a>() -> &'a String { &String::from("foo") }
// 上面代码是无效的:`'a` 存活的时间必须比函数的长。
// 这里的 `&String::from("foo")` 将会创建一个 `String` 类型,然后对它取引用。
// 数据在离开作用域时删掉,返回一个指向无效数据的引用。

方法

  • 方法标注和函数体类似。

trait

  • impl块可能也有生命周期的标注

约束

  • 泛型和生命周期相同,都可以使用约束。:字符意义为:
    T: 'a:在 T 中的所有引用都必须比生命周期 'a 活得更长。
    T: Trait + 'a:T 类型必须实现 Trait trait,并且在 T 中的所有引用都必须比 'a 活得更长。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use std::fmt::Debug; // 用于约束的 trait。

#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);
// `Ref` 包含一个指向泛型类型 `T` 的引用,其中 `T` 拥有一个未知的生命周期
// `'a`。`T` 拥有生命周期限制, `T` 中的任何*引用*都必须比 `'a` 活得更长。另外
// `Ref` 的生命周期也不能超出 `'a`。

// 一个泛型函数,使用 `Debug` trait 来打印内容。
fn print<T>(t: T) where
T: Debug {
println!("`print`: t is {:?}", t);
}

// 这里接受一个指向 `T` 的引用,其中 `T` 实现了 `Debug` trait,并且在 `T` 中的
// 所有*引用*都必须比 `'a'` 存活时间更长。另外,`'a` 也要比函数活得更长。
fn print_ref<'a, T>(t: &'a T) where
T: Debug + 'a {
println!("`print_ref`: t is {:?}", t);
}

fn main() {
let x = 7;
let ref_x = Ref(&x);

print_ref(&ref_x);
print(ref_x);
}

强制转换

  • 强制转换可以由编译器隐式推导,也可以通过声明不同生命周期的形式实现。
  • 通常情况之下是由较长的生命周期转换为较短的生命周期。

static

  • 'static是生命周期中最长的,它能够在整个程序运行过程中存在。它也可以被强制转换为一个更短的生命周期。

static生命周期的变量被保存在可执行文件的只读内存区。实现方法:

  • 使用static声明常量
1
static NUM: i32 = 18;//函数体之外
  • 产生拥有&'static str类型的string字面量

省略

  • 在某些普遍的状态之下rust可以省略生命周期的写法。可以在《Rust程序设计语言》中找到相关的解释。