Skip to main content

4认识所有权

[1][Rust语言入门视频教程,Rust编程入门视频教程(视频)](https://www.bilibili.com/video/BV1hp4y1k7SV)

[2][Rust 程序设计语言 简体中文版(文档)](https://kaisery.github.io/trpl-zh-cn/ch07-05-separating-modules-into-different-files.html)

4 认识所有权(重点)

  • 4.1. 什么是所有权?

  • 4.2. 引用与借用

  • 4.3. Slice 类型

4.1 什么是所有权?

一些语言中具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存;在另一些语言中,程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。在运行时,所有权系统的任何功能都不会减慢程序。

所有权规则(重点)
  1. Rust 中的每一个值都有一个被称为其 所有者owner)的变量。
  2. 值在任一时刻有且只有一个所有者。
  3. 当所有者(变量)离开作用域,这个值将被丢弃。
移动
let s1 = String::from("hello");
let s2 = s1;

println!("{}, world!", s1);
=>> 报错:value borrowed here after move

所有权从s1到s2了,s1所有权失效

4.2. 引用与借用

创建一个引用的行为称为 借用borrowing),借用不会获取值的所有权,只是引用该对象

示例

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()
}

以上代码的引用图示

&String s pointing at String s1

可变引用

只有可变引用才能被修改

fn main() {
let mut s = String::from("hello");

change(&mut s);
}

fn change(some_string: &mut String) {
some_string.push_str(", world");
}
限制

在同一时间只能有一个对某一特定数据的可变引用

let r1 = &mut s;
let r2 = &mut s;

=>> ❌ 重复引用
{
let r1 = &mut s;
} // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用

let r2 = &mut s;

=>> ✅

不可变与可变引用

在同一时刻也只能存在一个不可变或者可变引用

let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r3 = &mut s; // 大问题

println!("{}, {}, and {}", r1, r2, r3);

=>> ❌ 重复引用

注意:一个引用的作用域是从声明的地方开始一直持续到最后一次使用为止

let r1 = &s; // 没问题
let r2 = &s; // 没问题
println!("{} and {}", r1, r2);
// 此位置之后 r1 和 r2 不再使用

let r3 = &mut s; // 没问题
println!("{}", r3);

=>> ✅
悬垂引用

悬垂指针是其指向的内存可能已经被分配给其它持有者

fn main() {
let reference_to_nothing = dangle();
}

fn dangle() -> &String {
let s = String::from("hello");

&s
}

=>> ❌ 悬垂引用

将所有权移动出来

fn no_dangle() -> String {
let s = String::from("hello");

s
}

=>> ✅

4.3. Slice 类型

slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。slice 是一类引用,所以它没有所有权

let slice = &s[0..2];
let slice = &s[..2]; // 等效于0-2
let slice = &s[1..]; // 等效于1-2
let slice = &s[..]; // 等效于0-2

所有权

let mut s = String::from("hello world");

let word = first_word(&s);

s.clear(); // 错误!

println!("the first word is: {}", word);

=>> ❌ s.clear会尝试获取可变引用,而可变引用和不可变引用不能同时存在

word和s引用的内存空间有重叠,word是不可变引用,而s是可变引用

总结

所有权、借用和 slice 这些概念让 Rust 程序在编译时确保内存安全。Rust 语言提供了跟其他系统编程语言相同的方式来控制你使用的内存,但拥有数据所有者在离开作用域后自动清除其数据的功能意味着你无须额外编写和调试相关的控制代码。