Rust 是一门强调安全、并发、高效的系统编程语言。无 GC 实现内存安全机制、无数据竞争的并发机制、无运行时开销的抽象机制,是 Rust 独特的优越特性。 它声称解决了传统 C 语言和 C++语言几十年来饱受责难的内存安全问题,同时还保持了很高的运行效率、很深的底层控制、很广的应用范围, 在系统编程领域具有强劲的竞争力和广阔的应用前景。
在 Rust 笔记(三)中,讲了复核类型,本文就认识一下 Rust 中的变量。
可变 & 不可变
Rust 默认支持类型推导,在编译器能够推导类型的情况下,变量类型一般可以省略,但常量(const)和静态变量(static)必须声明类型。
图片
let a: &str = "一个不可变变量";
const B: &str = "一个常量";
static C: &str = "一个静态变量";
变量默认是不可变的。如果需要让变量具有可变性,必须为变量添加 mut 关键字。默认变量不可变是一个很重要的特性,它符合最小权限原则(Principle of Least Privilege),有助于我们写出健壮且正确的代码。当你使用 mut 却没有修改变量,Rust 编译期会友好地报警,提示你移除不必要的 mut。
let mut x = 10; // 声明一个可变的整型变量
x = 20; // 可以修改 x 的值
当使用 mut 关键字声明一个引用时,该引用就可以被修改所指向的变量,例如:
let mut s = String::from("hello");
let r = &mut s; // 声明一个可变的字符串引用
r.push_str(" world!"); // 可以修改 s 的值
在其他大多数语言中,要么只支持申明可变的变量,要么只支持申明不可变的变量,但是 Rust 就不一样了,两者我都要,既要灵活性又要安全性。还有一个很大的优点,那就是运行性能上的提升,因为将本身无需改变的变量声明为不可变在运行期会避免一些多余的 runtime 检查。
选择可变还是不可变,更多的还是取决于你的使用场景,
- 不可变: 有安全性,但是丧失了灵活性和性能
- 可变变量:最大的好处就是使用上的灵活性和性能上的提升
使用下划线开头忽略未使用的变量
在 Rust 中创建了一个变量却不在任何地方使用它,Rust 会给你一个警告,因为这可能会是个 BUG。
图片
但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头:
图片
变量绑定
在其他语言中,比如 JS,我们使用 var a = "Hello World"。意思是将 "Hello World" 赋值给变量 a。在 Rust 中,let a = "Hello World",意思和 JS 中一样,但是这个过程起了有一个名字:变量绑定。
其实本质上是一回事,赋值 === 绑定,但是 Rust 存在“所有权”这一特性,所以绑定的含义更清晰准确。绑定就是把这个对象绑定给一个变量,让这个变量成为它的主人。
变量解构 & 解构赋值
let 表达式不仅仅用于变量的绑定,还能进行复杂变量的解构:从一个相对复杂的变量中,匹配出该变量的一部分内容:
let (a, mut b): (bool, bool) = (true, false);
// a = true,不可变; b = false,可变
println!("a = {:?}, b = {:?}", a, b);
也可以进行变量的解构赋值:
let (a, b, c, d, e);
(a, b) = (1, 2);
变量遮蔽
Rust 允许对申明的变量再次声明,也就是允许申明相同的变量,后面的变量就会遮蔽前端的变量。
let x: i32 = 1;
let x: i32 = 2;
let x: i32 = x +1;
println!("x: {}", x);
这和 mut 变量的使用是不同的,第二个 let 生成了完全不同的新变量,两个变量只是恰好拥有同样的名称,涉及一次内存对象的再分配 ,而 mut 声明的变量,可以修改同一个内存地址上的值,并不会发生内存对象的再分配,性能要更好。
变量命名
在命名方面,和其它语言没有区别,不过当给变量命名时,需要遵循 Rust 命名规范。详情可看RFC 430
- type-level 的构造 Rust 倾向于使用驼峰命名法,value-level 的构造使用蛇形命名法。
- 特殊命名:名称应该使用动词,而不是形容词或者名词。
- 类型转换要遵守 as_,to_,into_ 命名惯例(C-CONV)。
- 读访问器(Getter)的名称遵循 Rust 的命名规范(C-GETTER)。
- 一个集合上的方法,如果返回迭代器,需遵循命名规则:iter,iter_mut,into_iter (C-ITER)。
- 迭代器的类型应该与产生它的方法名相匹配(C-ITER-TY)。
- Cargo Feature 的名称不应该包含占位词(C-FEATURE)。
- 命名要使用一致性的词序(C-WORD-ORDER)
变量和常量之间的差异
有变量就有常量。常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异:
- 常量不允许使用 mut。常量不仅仅默认不可变,而且自始至终不可变,因为常量在编译完成后,已经确定它的值。
- 常量使用 const 关键字而不是 let 关键字来声明,并且值的类型必须标注。
let a: &str = "一个不可变变量";
const B: &str = "一个常量";
常量可以在任意作用域内声明(包括全局作用域),在声明的作用域内,常量在运行的整个过程中都有效。对于需要在多处代码共享一个不可变的值时非常有用。
参考
- https://zhuanlan.zhihu.com/p/615270800
- https://time.geekbang.org/column/article/411632
- https://www.runoob.com/rust/rust-function.html
- https://zhuanlan.zhihu.com/p/366756163
- https://course.rs/practice/naming.html