Rust设计之形
参考视频:https://www.bilibili.com/video/BV1564y1177A
Rust要解决的问题
先看以下C语言的代码:
typedef struct Dummy { int a; int b; } Dummy;
void foo(void)
{
Dummy *ptr = (Dummy *)malloc(sizeof(struct Dummy));
Dummy *alias = ptr;
free(ptr);
int a = alias.a; // Use afoter free
free(alias); // Double free
}
- 可以看到变量
alias
使用了已经被释放的内存,结果是不可控的
Rust解决方案:所有权+借用
fn main() {
let x = String::from("hello");
let y = x;
// err[E0382]: use of moved value: `x`
drop(x);
}
- Rust将堆上的数据视为资源,每个资源都只能有唯一的所有者
- 能够从资源的所有者处借用资源
- 当资源被其他代码借用时,资源的所有者不可以释放资源或修改资源
- 编译时检查,无运行时开销
借用规则
- 借用可以分为可变借用、只读(共享)借用
- 不允许对不可变资源进行可变借用
- 允许同时存在多个对同一对象的只读借用,但是可变借用拥有对被借用对象的独占权
- 借用的生命周期需要小于被借用资源的生命周期
只读借用(&)
struct Dummy { a: i32, b: i32 }
fn foo() {
let mut res = Box::new(Dummy{
a: 0,
b: 0
});
take(&res);
res.a = 2048;
}
// res 资源被arg只读借用
fn take(arg: &Box<Dummy>) {
arg.a = 2048; // 编译错误:Cannot mutate via an immutable reference
} // 资源仍然被res持有,take返回时不会释放
可变借用(&mut)
struct Dummy { a: i32, b: i32 }
fn foo() {
let mut res = Box::new(Dummy{
a: 0,
b: 0
});
take(&mut res);
res.a = 4096;
let borrower = &mut res;
let alias = &mut res; // 不允许存在多个对同一资源的可变借用
}
fn take(arg: &mut Box<Dummy>) {
arg.a = 2048;
}
可变性
- 类似函数式语言,Rust中所有的资源都默认不可变
- 需要使用mut关键字声明某个资源为可变的
struct Dummy { a: i32, b: i32 }
fn foo() {
let res = Box::new(Dummy{
a: 0,
b: 0
});
res.a = 2048; // 错误:资源时不可变的
}
生命周期
- 编译器使用生命周期对代码逻辑进行限制:借用的存活时间不应该比被借用对象长。只要这一规则能够贯彻,那么程序将不可能存在悬垂指针问题
- 生命周期可以理解为变量作用域的长度,变量a比变量b“活得长”即a的作用域能够覆盖b的作用域
let x = 0;
let y = &x;
let z = &y;
=》自动类型推导,自动声明周期推导,解开语法糖
'a: {
let x: i32 = 0;
'b: {
let y: &'b i32 = &'b x;
'c: {
let z: &'c &'b i32 = &'c y;
}
}
}
拒绝悬垂指针
fn as_str(data: &u32) -> &str {
let s = format!("{}", data);
&s
}
fn main() {
let x: u32 = 0;
println!("{}", as_str(&x));
}
推导:
fn as_str<'a>(data: &'a u32) -> &'a str {
'b {
let s = format!("{}", data);
return &'b s
}
}
fn main() {
'c: {
let x: u32 = 0;
'd: {
// 生成一个新生命周期 ’d,因为对 x 的借用并不需要
// 在整个 ‘c 生命周期中均有效。as_str所返回的引用的生命周期需要与 ’d 相同,然而这显然是不可能的
println!("{}", as_str<'d>(&'d x));
}
}
}
泛型与Trait
- Rust使用Trait对类型的行为进行抽象
- 组合优于继承
Trait的作用:
- 接口抽象
- 泛型参数约束
- 类型标签(Copy、Clone)
- 抽象类型