0%

2024春夏季开源操作系统训练营第一阶段总结-黄文轩

2024开源操作系统训练营第一阶段总结-黄文轩

本文是对OS训练营第一阶段的总结,重点谈谈在第一阶段学习Rust的过程中,印象比较深刻的一些知识点。

所有权

所有权是Rust中一个非常重要的特性,查看如下代码:

1
2
3
4
5
6
fn main() {
let s1 = String::from("Hello World");
let s2 = s1;

println!("{s2}");
}

执行let s2=s1语句后,s1就不能再使用了,也就是说s1将资源移动给了s2,原来的变量就不再用原先的值了。Rust明确了所有权的概念,如果一个变量拥有了一个资源的所有权,就要负责那个资源的回收和释放,主要目的是为了安全。

在使用函数时,也同样要注意:

1
2
3
4
5
6
7
8
9
fn foo(s: String) {
println!("{s}")
}

fn main() {
let s1 = String::from("Hello World");
foo(s1); // 所有权发生移动
foo(s1); // 出错
}

在第一次使用foo时,s1字符串的所有权就移动到了参数s上,在该函数回收栈空间时,这个字符串就被回收掉了,因此无法再打印这个字符串。

如下代码就不存在问题,因为foo将所有权转移出来:

1
2
3
4
5
6
7
8
9
10
fn foo(s: String) -> String {
println!("{s}");
s
}

fn main() {
let s1 = String::from("Hello World");
let s1= foo(s1);
foo(s1);
}

但是上述代码将所有权转移出来,有时还是麻烦了点,频繁转移所有权会显得很冗余,可以使用引用这个特性。

下述代码使用了引用:

1
2
3
4
5
6
7
8
9
fn foo(s: &String) {
println!("{s}");
}

fn main() {
let s1 = String::from("Hello World");
foo(&s1);
foo(&s1);
}

Box智能指针

如果一个变量,存的是另一个变量在内存的地址值,那么就被称为指针。普通指针是对值的借用,而智能指针通常拥有对数据的所有权,可以做一些额外操作,比如管理引用计数和资源自动回收。

标准库中常见的智能指针:

  • Box<T>:在堆内存上分配值
  • Rc<T>:启用多重所有权的引用计数类型
  • Ref<T>&RefMut<T>,通过RefCell<T>访问:在运行时而不是编译时强制借用规则的类型

本文着重介绍Box,即指向堆内存的智能指针,允许在堆上存储数据而非栈,没有其他性能开销。

常用场景:在编译时,某类型的大小无法确定。但使用该类型时,上下文却要知道它的确切大小。

1
2
3
4
fn main() {
let b = Box::new(5); // 存储在堆上
println!("b = {}",b);
}

在变量 b 在离开作用域时,同样会被释放。

同样可以作用于结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Point {
x: u32,
y: u32
}

fn foo() -> Box<Point> {
let p = Point {x:10, y:20};
Box::new(p)
}

fn main() {
// ....
}

在创建Point结构体时,其尺寸是固定的,所以它的实例会被创建在栈上。而在创建 Box<Point> 实例的时候会发生所有权转移:资源从栈上移动到了堆,原来栈的那片资源被置为无效。

可以对Box进行解引用,由于u8具有copy语义,所以解引用后,原来的boxed还能使用

1
2
3
4
5
6
7
fn main() {
let boxed: Box<u8> = Box::new(5);
let val: u8 = *boxed;

println!("{:?}",val);
println!("{:?}",boxed);
}

但是对于move语义的类型来说,就不适用了
Box同样可以作为函数参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#[derive(Debug)]
struct Point {
x: u32,
y: u32
}

fn foo(p: Box<Point>) {
println!("{:?}",p);
}

fn main() {
let b: Box<Point> = Box::new(Point{x:10, y:20});

foo(b)
}