文章目录
变量
- Rust 支持声明可变的变量和不可变的变量,需要手动设定变量的可变性;
- 相较于其他语言的变量赋值,Rust 叫变量绑定;
不可变变量
设置变量 a 为字符串 “hello world”,相较于 go
的 var,在 rust 中使用 let,为了突出变量绑定的概念
let a = "hello world"
上述默认情况下,变量是不可变的,如下,编译会出问题,cannot assign twice to immutable variable。(无法对不可变的变量进行重复赋值)
fn main() {
let x = "abc";
println!("The value of x is: {}", x);
x = "def"; // 修改不可变的变量
println!("The value of x is: {}", x);
}
可变变量
可以通过 mut
关键字让变量变为可变 的,如下
fn main() {
let mut x = "abc"; // 加上 mut,x 为可变变量
println!("The value of x is: {}", x);
x = "def";
println!("The value of x is: {}", x);
}
注:mut只是可以修改变量,而不能修改类型。
使用下划线开头忽略未使用的变量
如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告。但是有时创建一个不会被使用的变量是有用的。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头:
fn main() {
let _x = 5;
let y = 10;
}
使用 cargo run
运行下试试:
# cargo run
Compiling hello_world v0.1.0 (/opt/rust/hello_world)
warning: unused variable: `y`
--> src/main.rs:4:9
|
4 | let y = 10;
| ^ help: if this is intentional, prefix it with an underscore: `_y`
|
= note: `#[warn(unused_variables)]` on by default
warning: `hello_world` (bin "hello_world") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.35s
Running `target/debug/hello_world`
可以看到,两个变量都没有使用,但是编译器却独独给出了 y
未被使用的警告。
注意:和 go 不同,go 编译报错,rust 只是编译 warning,可以运行。
变量解构
let
表达式不仅仅用于变量的绑定,还能进行复杂变量的解构:
fn main() {
let (a, mut b): (bool,bool) = (true, false);
// a = true,不可变; b = false,可变
println!("a = {:?}, b = {:?}", a, b);
b = true;
assert_eq!(a, b);
}
解构式赋值
我们可以在赋值语句的左式中使用元组、切片和结构体模式。
struct Struct {
e: i32
}
fn main() {
let (a, b, c, d, e);
(a, b) = (1, 2);
// _ 代表匹配一个值,但是我们不关心具体的值是什么,因此没有是一个变量名而是使用了 _
[c, .., d, _] = [1, 2, 3, 4, 5];
Struct { e, .. } = Struct { e: 5 };
assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
}
这种使用方式跟之前的 let
保持了一致性,但是 let
会重新绑定,而这里仅仅是对之前绑定的变量进行再赋值。
需要注意的是,使用 +=
的赋值语句还不支持解构式赋值。
变量和常量之间的差异
常量(constant)。与不可变变量一样,常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异:
- 常量不允许使用
mut
。常量不仅仅默认不可变,而且自始至终不可变,因为常量在编译完成后,已经确定它的值。 - 常量使用
const
关键字而不是let
关键字来声明,并且值的类型必须标注。
下面是一个常量声明的例子,其常量名为 MAX_POINTS
,值设置为 100,000
。(Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,另外对数字字面量可插入下划线以提高可读性):
const MAX_POINTS: u32 = 100_000;
变量遮蔽(shadowing)
Rust 允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的,如下所示:
fn main() {
let x = 5;
let x = x + 1;
println!("The value of x is: {}", x);
}
这个程序首先将数值 5
绑定到 x
,然后通过重复使用 let x =
来遮蔽之前的 x
,并取原来的值加上 1
,所以 x
的值变成了 6
。当运行此程序,将输出以下内容:
$ cargo run
...
The value of x is: 6
这和
mut
变量的使用是不同的,第二个let
生成了完全不同的新变量,两个变量只是恰好拥有同样的名称,涉及一次内存对象的再分配
,而mut
声明的变量,可以修改同一个内存地址上的值,并不会发生内存对象的再分配。
基本类型
Rust 像 go 一样编译时需要知道所有变量的类型,也可以自己推断导出变量的类型。
通常需要给一个显式的类型标注,例:
let guess: i32 = 32;
let x = "42".parse::<i32>();
数值类型
类型转换必须是显式的
Rust 数值可以使用方法,如浮点数取整 13.14_f32.round()
整数类型
长度 | 有符号类型 | 无符号类型 |
---|---|---|
8 位 | i8 | u8 |
16 位 | i16 | u16 |
32 位 | i32 | u32 |
64 位 | i64 | u64 |
128-位 | i128 | u128 |
视架构而定 | isize | usize |
cpu 是 32 位则 32位,cpu 是 64 为则为 64 位
和其他语言一样
- 二进制 0b1010
- 八进制 0o7654
- 十六进制 0xfe
- 字节 b’A’
Rust 整形默认使用 i32
整形溢出
在 debug 模式编译时,Rust 会检查整形的溢出,溢出会触发 panic
在 release 模式构建时,Rust 不会检查整形的溢出,按照补码循环溢出的规则处理,即 255 + 1 = 0
浮点类型
两种基本类型,32 位大小的 f32 和 64 位大小的 f64;
默认使用 f64。浮点数根据 IEEE-754 标准实现。f32 类型是单精度浮点型,f64 为双精度。
NaN
对于数学上未定义的结果,如取负数平方根,会产生特殊结果,Rust 的浮点数类型使用 NaN (Not a number) 来处理。
所有跟 NaN 交互的操作,都会返回一个 NaN,而且 NaN 不能用来比较;会触发 panic
可以使用 is_nan() 方法来判断数值是否是 NaN
数字运算和位运算
支持所有数字类型的基本数学运算:加法(+)、减法(-)、乘法(*)、除法(/)和取模(%)运算。
位运算 与(&)、或(|)、异或(^)、位非(!)、左移(<<)、右移(>>)。
序列 Range
用于生成连续的数值,类似于 python 的 range(),1..5
为 1 到 4,1..=5
为 1 到 5
如 1 到 5,Python 中 range(1, 6);在 Rust 中
for i in 1..=5 {
println!("{}",i);
}
只能应用与整数或字符类型,因为整数和字符类型是连续的。
有理数和复数
有理数和复数不在标准库,社区提供 Rust 数据库:num
使用方法:
在 Cargo.toml 中的 [dependencies] 下添加一行 num = “0.4.0”
代码中使用 num:
use num::complex::Complex;
fn main() {
let a = Complex { re: 2.1, im: -1.2 };
let b = Complex::new(11.1, 22.2);
let result = a + b;
println!("{} + {}i", result.re, result.im)
}
字符、布尔、单元类型
字符类型占用 4 个字节,Rust 中使用 ‘’ 来表示
布尔类型有两个可能的值:true 和 false,布尔值占用内存的大小为 1 个字节:
单元类型就是 (),唯一的值也是 ()
比如,你可以用 () 作为 map 的值,表示我们不关注具体的值,只关注 key。 这种用法和 Go 语言的 struct{} 类似,可以作为一个值用来占位,但是完全不占用任何内存。
语句、表达式
Rust 的函数体是由一系列语句组成,最后由一个表达式来返回值,例如:
fn add_with_extra(x: i32, y: i32) -> i32 {
let x = x + 1; // 语句
let y = y + 5; // 语句
x + y // 表达式
}
前两行是语句,最后一行是表达式,相较于其他语言,Rust 需要区分语句和表达式
语句
完成了一个具体的操作,语句没有返回值,语句不能赋值给其他值。
表达式
表达式会进行求值,然后返回一个值;表达式不能包含分号。
在表达式后加上分号,它就会变成一条语句,不会返回一个值。如果不返回任何值,会隐式地返回一个 ()
函数
例子,和其他语言几乎一样。
fn add(i: i32, j: i32) -> i32 {
i + j
}
Rust 是强类型语言,因此需要为每一个函数参数都标识出它的具体类型。
函数就是表达式,有返回值,可以直接将返回值赋给调用者,函数的返回值就是最后一条表达式的返回值,也可以 return 提前返回。
特殊返回类型
无返回值()
例如单元类型 (),是一个零长度的元组。它没啥作用,但是可以用来表达一个函数没有返回值:
- 函数没有返回值,那么返回一个 ()
- 通过 ; 结尾的表达式返回一个 ()
发散函数
当用 ! 作函数返回类型的时候,表示该函数永不返回( diverge function ),特别的,这种语法往往用做会导致程序崩溃的函数。
使 panic
fn dead_end() -> ! {
panic!("end!");
}
使无限循环
fn forever() -> ! {
loop {
//...
};
}
参考:Rust
大多数为个人学习笔记,比较简化。