Featured image of post 初学Rust

初学Rust

为什么要有Rust

通过各方面学习了解到了Rust这个语言.

Rust学习之为什么有Rust

1. 解决什么问题

首先有GC(垃圾回收)的语言性能不够极致, 而无GC差不多就只有C与C++, 可这两个语言在运行阶段容易出现空指针或野指针的情况, 并且无法在编译期间就检查出来.

综上, 那么Rust需要解决的就是无GC+无空指针:

2. 无GC好办:

​ 为啥有GC, 我们的程序运行时会有栈与堆, 栈是存放函数与变量的, 堆是用来存放大型变量或可变变量的. 而由于我们存放在栈里的资源较小, 当我们共享变量时, 直接复制一份即可; 可堆中的变量极大, 复制一份性能消耗大. 因此我们采用指针指向同一份堆资源的方式来实现共享变量, 可如果多个指针指向同一份堆变量时, 我们应该如何确定什么时候这个变量应该被释放.

​ 首先最容易想到的, 就是当最后一个指针取消指向时, 这个时候就释放资源, 可是则便是GC的一个原理, 也即有一个程序定时的去检查有无栈中的指针指向堆中的变量, 如果没有则释放.

​ 那既然则时机不好把握, 而我们又必须实现变量共享(没有变量共享的编程语言没意义); 于是我们可以设定, 当有新的指针指向时(一般是原来的指针赋值给新的指针), 这个时候我们便认为"所有权“发生了转移, 也即原来的指针失效, 以新指针为准, 而当我们的指针离开作用域时便释放资源.

​ 可是这样的话, 如果我们需要在不同作用域传参时, 不就马上失效了吗? 因此我们引入了”借用"(其实就是引用), 也即用堆上的空间指向我们当前拥有所有权的堆指针, 如此一来由于该借用并不直接指向堆空间, 因此不会释放堆资源.

​ 但是, 如果我们多个借用都可以修改堆空间的话, 就像同时读写一样, 会导致内存污染. 因此我们设定了"可变借用“与”不可变借用", 可变借用也即写锁, 同一时刻只能有一个可变借用, 同时有了可变借用也不能有不可变借用; 如果是不可变借用, 则可同时有多个不可变借用.

​ 到目前为止也就实现了"无GC".

3. 接下来就是空指针问题:

​ 对于这个问题, 我们引入"生命周期", 也即, 我们的借用的生命周期, 不能长于原始借用对象的生命周期; 这一点编译器会替我们检查, 不过有些情况编译器无法智能识辨, 例如我传入多个借用, 让后依据一些条件返回不同的借用, 这个时候, 编译器并不知道函数逻辑, 他只知道返回的一个与传入的两个有关, 但是到底怎么相关, 编译器不知道, 因此需要我们手动标注. (离开作用域, 则生命周期结束)

4. 总结

对于上面这些初学概念, 我认为上手rust最难的应该是"生命周期", 不过我认为目前可以先写函数然后通过与哪些有关联的方式来标注, 亦或是直接让ai助手根据上下文帮我们标注.

5. 特别补充

rust中有String, &str, 和&String:

  • String很好理解可变字符串, 在栈上占用24字节, 分别存放堆地址指针, 容量, 长度.
  • &String也很好理解, 在栈上占用8字节, 存放指向的String栈地址.
  • &str得好好理解, 可理解为不可变数组, 在栈上占用16字节, 分别存放堆地址指针和长度.

当变量拥有借用时, 且借用生命周期未结束, 则无法转移所有权:

1
2
3
4
5
6
fn main() {
    let mut t1 = String::from("Hello, world!");
    let t2 = &t1[..];  // &str
    let t3 = &t1;  // &String
    let t4 = t1;  // 失败!!!
}

当变量拥有不可变借用时, 且未失效时, 原有所有权的变量也无法更改(被冻结了):

1
2
3
4
5
fn main() {
    let mut t1 = String::from("Hello, world!");
    let t2 = &t1[..];  // 或 let t3 = &t1;
    t1.push_str(", world");    // 失败!!!
}

当变量拥有可变借用时, 且未失效时, 原有所有权的变量既无法读也无法写:

1
2
3
4
5
6
fn main() {
    let mut t1 = String::from("Hello, world!");
    let t2 = &mut t1;
    println!("{}", t1);  // 失败!!!
    t1.push_str(", world");    // 失败!!!
}

有了可变借用, 就不能有不可变借用, 反之亦然, 可变不会降级为不可变:

1
2
3
4
5
fn main() {
    let mut t1 = String::from("Hello, world!");
    let t2 = &mut t1;
    let t3 = &t1;  // 失败!!!
}
使用 Hugo 构建
主题 StackJimmy 设计