Skip to content

Rust基础知识-所有权

目录

变量

而在声明变量与常量的时候, 个人感觉是 Rust 与其他语言最为不同的地方, 先看下面的例子, 再做进一步的分析:

fn main(){
  let x = 1;
  let x = 'aaa';
  let mut y = 1;
}

在 Rust 中, 使用 let 默认设定为不可变量, 即变量不可以被再次赋值, 如果想要可变, 则需要使用 let mut 进行赋值。 有点奇怪哈, 其他语言都在尽可能的精简, 例如 Java 中 int 即可声明一个变量, 如果要求一个值不可变, 需要写 final int 才可以。

但是, Rust 这种处理方式无疑是正确的, 因为程序中大多数变量都是朝生夕死的(这也是 GC 设置多个区间的主要原理之一), 这种变量本身也很少会产生多次重复的变化, 因此采用不可变方式是非常有利于程序安全的。

而代码中另外一个问题则是, 不可变变量被多次赋值, 两次类型还并不一致, 这个在 rust 中被称为 变量的 Shadowing。 意思是第二个变量遮盖了第一个变量。这样在很多时候 变量就无需重复想名字, 并且可以根据变量被赋值的时候来判断当前变量到底是什么值了。

所有权

接下来就到了本文的关键部分: 所有权。 这是一个在其他编程语言未曾提及的名字, 由于其他编程语言在创建一个变量的时候, 这个变量可以随意的被传递给函数或被再次赋值给其他变量, 而在 rust 中, 一个变量值 同一时间只能被一个变量名称所持有(好绕口的关系, 仿佛在说绕口令)。举例来说:

fn main(){
  let a = String::from("i'm a");
  let b = a;
  println!("a is {a}");
}

在上面这段例子中, 名称为a的变量定义一个字符串变量, 字符串变量的值为 i'm a, 随后 b = a 中, 值从 a 的身上移动到了 b 的身上。 此时 a 不再具有对应的值, 导致 println!("a is {a}"); 会报错。 这就是所有权错误。

所有权由三条基础定义:

  1. Rust中, 每一个值都有一个所有者。
  2. 任何一个时刻, 一个值都只有一个所有者。
  3. 当所有者所在作用域 (scope) 结束的时候, 其管理的值会被一起释放掉。

这里除了涉及到所有权, 也涉及到了作用域, 而作用域这个概念与其他语言基本类似: 从创建开始, 到这个变量所在到大括号结束的位置。

当变量存在于作用域内时, 变量是有效的, 当变量离开作用域时, 变量无效。

借用与引用

尽管所有权可以很好的解决变量乱飞的问题, 但是也带来一个新的问题: 变量不可能一直在一个地方, 例如传入传出函数.

是的, 在 rust 里面, 调用函数也会转移变量的所有权, 也就是一个变量直接传入函数, 如果不接受函数的返回值或者函数不存在返回值的话, 他就没办法继续使用了。 因为他在函数内部被使用之后, 生命周期结束, 随着函数一起被释放了。

因此 rust 引入了借用与引用。 借用与引用是一体两面, a 借用了, 也就是 a 被引用了。

在rust中, 借用主要如下所示:

fn main(){
  let x = String::from("x");
  let mut y = string::from("y");
  let x1 = &x;
  let y1 = &mut y;
  let y2 = &y;
}

rust 使用 & 进行借用, 同时使用 * 可以进行解引用。同时可以使用多个 & 或多个 * 来进行多级借用与多级解引用。

总结借用与引用有以下几个特点:

  1. 引用要小于所有权的变量作用域
  2. 同一时间只能存在一个可变引用
  3. 不可变引用可以复制多份, 同时存在多个
  4. 引用变量和所有权变量作用域不能重叠
  5. 引用变量之间作用域不能重叠
  6. 有借用存在的情况下, 不能对原所有权变量进行更新, 借用完成后, 就可以继续进行更新操作。