1. 变量与可变性

Rust 中变量默认是不可变的(immutable),这是 Rust 安全性的重要基础。

1.1 不可变变量(默认)

1
2
3
4
5
6
7
fn main() {
let x = 5;
println!("x 的值是:{}", x);

// x = 6; // 编译错误!不能对不可变变量二次赋值
// error[E0384]: cannot assign twice to immutable variable `x`
}

1.2 可变变量(mut)

使用 mut 关键字声明可变变量:

1
2
3
4
5
6
7
8
9
10
fn main() {
let mut x = 5;
println!("x 的值是:{}", x);

x = 6; // 正确!x 是可变的
println!("x 的值现在是:{}", x);

// 可变变量也有类型约束,不能改变类型
// x = "hello"; // 编译错误!不能将 &str 赋给 i32 类型
}

1.3 未使用变量

1
2
3
4
5
6
7
8
9
10
fn main() {
// 未使用的变量会产生警告
// let y = 10; // warning: unused variable: `y`

// 使用下划线前缀忽略警告
let _y = 10; // 不会产生警告

// 单独的下划线表示完全忽略
let _ = 42; // 值会立即被丢弃
}

1.4 延迟初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let x; // 声明但不初始化

// 在使用之前必须初始化
x = 5;

println!("x = {}", x);

// 注意:如果在某些分支中没有初始化,编译器也会报错
let y;
if true {
y = 1;
} else {
y = 2;
}
println!("y = {}", y);
}

2. 变量遮蔽(Shadowing)

Rust 允许在同一作用域中用 let 重新声明同名变量,新变量会”遮蔽”之前的变量。

2.1 基本遮蔽

1
2
3
4
5
6
7
8
9
10
fn main() {
let x = 5;
println!("第一个 x = {}", x); // 5

let x = x + 1; // 遮蔽前一个 x
println!("第二个 x = {}", x); // 6

let x = x * 2; // 再次遮蔽
println!("第三个 x = {}", x); // 12
}

2.2 遮蔽可以改变类型

遮蔽与 mut 不同,遮蔽允许改变变量的类型:

1
2
3
4
5
6
7
8
9
10
fn main() {
// 遮蔽允许改变类型
let spaces = " "; // &str 类型
let spaces = spaces.len(); // usize 类型,变量名相同但类型不同
println!("空格数量:{}", spaces); // 3

// 而 mut 不允许改变类型
// let mut spaces = " ";
// spaces = spaces.len(); // 编译错误!类型不匹配
}

2.3 作用域中的遮蔽

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let x = 5;

{
let x = x * 2; // 内部作用域遮蔽外部的 x
println!("内部作用域 x = {}", x); // 10
}

// 离开内部作用域后,x 恢复为外部的值
println!("外部作用域 x = {}", x); // 5
}

2.4 遮蔽 vs mut 的区别

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
// 遮蔽:创建了新变量,旧变量不再可访问
let x = 5;
let x = "hello"; // OK:创建了新变量
println!("x = {}", x);

// mut:同一个变量,可以修改值,但不能改变类型
let mut y = 5;
y = 10; // OK:修改值
// y = "hello"; // 编译错误:类型不匹配
println!("y = {}", y);
}

3. 常量与静态变量

3.1 常量(const)

常量在编译时求值,必须标注类型,可以在任何作用域中声明(包括全局作用域)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 全局常量(命名惯例:全大写 + 下划线分隔)
const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.141_592_653_589_793;
const GREETING: &str = "Hello, Rust!";

// 常量可以使用编译期可计算的表达式
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

fn main() {
println!("最大点数:{}", MAX_POINTS);
println!("圆周率:{}", PI);
println!("问候语:{}", GREETING);
println!("三小时的秒数:{}", THREE_HOURS_IN_SECONDS);

// 常量可以在函数内部声明
const LOCAL_CONST: i32 = 42;
println!("局部常量:{}", LOCAL_CONST);
}

3.2 静态变量(static)

静态变量拥有 'static 生命周期,在程序运行期间始终存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 不可变静态变量
static LANGUAGE: &str = "Rust";
static VERSION: u32 = 2021;

// 可变静态变量(使用时需要 unsafe 块)
static mut COUNTER: u32 = 0;

fn main() {
println!("语言:{},版本:{}", LANGUAGE, VERSION);

// 访问和修改可变静态变量是 unsafe 的
// 因为多线程同时修改会导致数据竞争
unsafe {
COUNTER += 1;
println!("计数器:{}", COUNTER);
}
}

3.3 const vs static 的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const CONST_VALUE: i32 = 42;
static STATIC_VALUE: i32 = 42;

fn main() {
// const: 编译时内联替换,没有固定内存地址
// static: 有固定内存地址,整个程序生命周期存在

// 可以获取 static 的引用(有固定地址)
let ref_to_static: &i32 = &STATIC_VALUE;
println!("static 的引用:{}", ref_to_static);

// const 在使用时会被内联到代码中
println!("const 值:{}", CONST_VALUE);

// 区别总结:
// 1. const 没有固定内存地址,static 有
// 2. const 在编译时内联,static 在运行时从固定地址读取
// 3. static 可以是 mut 的(需要 unsafe),const 不能
// 4. static 可以存储包含内部可变性的类型(如 Mutex)
}

4. 基本数据类型

Rust 是静态类型语言,所有变量的类型在编译时必须确定。

4.1 整数类型

长度 有符号 无符号 范围
8-bit i8 u8 i8: -128 到 127, u8: 0 到 255
16-bit i16 u16 i16: -32768 到 32767
32-bit i32 u32 i32: 约 -21 亿到 21 亿
64-bit i64 u64 i64: 约 -9.2e18 到 9.2e18
128-bit i128 u128 非常大的范围
arch isize usize 取决于平台(32位或64位)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
fn main() {
// 默认整数类型是 i32
let default_int = 42; // i32

// 显式标注类型
let x: i8 = 127; // i8 最大值
let y: u8 = 255; // u8 最大值
let z: i16 = -32_768; // i16 最小值
let a: u32 = 4_294_967_295; // u32 最大值
let b: i64 = -9_223_372_036_854_775_808; // i64 最小值
let c: u128 = 340_282_366_920_938_463_463_374_607_431_768_211_455; // u128 最大值

// isize/usize 取决于系统架构
let index: usize = 10; // 用于数组索引
let ptr_size: isize = -1; // 指针大小的有符号整数

println!("默认 i32: {}", default_int);
println!("i8: {}, u8: {}", x, y);
println!("i16: {}", z);
println!("u32: {}", a);
println!("i64: {}", b);
println!("u128: {}", c);
println!("usize: {}, isize: {}", index, ptr_size);

// 查看类型大小
println!("\n各整数类型的字节数:");
println!("i8/u8: {} 字节", std::mem::size_of::<i8>());
println!("i16/u16: {} 字节", std::mem::size_of::<i16>());
println!("i32/u32: {} 字节", std::mem::size_of::<i32>());
println!("i64/u64: {} 字节", std::mem::size_of::<i64>());
println!("i128/u128: {} 字节", std::mem::size_of::<i128>());
println!("isize/usize: {} 字节", std::mem::size_of::<isize>());
}

4.2 整数字面量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
fn main() {
// 十进制
let decimal = 98_222; // 下划线分隔提高可读性

// 十六进制(0x 前缀)
let hex = 0xff; // 255

// 八进制(0o 前缀)
let octal = 0o77; // 63

// 二进制(0b 前缀)
let binary = 0b1111_0000; // 240

// 字节(仅限 u8,b 前缀 + 单字符)
let byte = b'A'; // 65

// 类型后缀
let typed = 42u8; // u8 类型的 42
let typed2 = 1_000_i64; // i64 类型的 1000

println!("十进制: {}", decimal); // 98222
println!("十六进制: 0xff = {}", hex); // 255
println!("八进制: 0o77 = {}", octal); // 63
println!("二进制: 0b1111_0000 = {}", binary); // 240
println!("字节: b'A' = {}", byte); // 65
println!("类型后缀: {}u8, {}_i64", typed, typed2);

// 不同进制表示同一个数
let same_number_dec = 255;
let same_number_hex = 0xff;
let same_number_oct = 0o377;
let same_number_bin = 0b1111_1111;

assert_eq!(same_number_dec, same_number_hex);
assert_eq!(same_number_hex, same_number_oct);
assert_eq!(same_number_oct, same_number_bin);
println!("\n255 的不同表示方式都相等!");
}

4.3 整数溢出处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
fn main() {
// 在 Debug 模式下,整数溢出会导致 panic
// 在 Release 模式下,整数溢出会进行二进制补码回绕

// Rust 提供了显式处理溢出的方法:
let a: u8 = 255;

// 1. wrapping_* 方法:回绕(溢出时绕回)
let wrapped = a.wrapping_add(1);
println!("wrapping_add: 255 + 1 = {}", wrapped); // 0

let wrapped2 = a.wrapping_add(20);
println!("wrapping_add: 255 + 20 = {}", wrapped2); // 19

// 2. checked_* 方法:返回 Option,溢出时返回 None
let checked = a.checked_add(1);
println!("checked_add: 255 + 1 = {:?}", checked); // None

let checked2: Option<u8> = 200u8.checked_add(50);
println!("checked_add: 200 + 50 = {:?}", checked2); // None

let checked3 = 100u8.checked_add(50);
println!("checked_add: 100 + 50 = {:?}", checked3); // Some(150)

// 3. overflowing_* 方法:返回 (结果, 是否溢出) 元组
let (result, overflowed) = a.overflowing_add(1);
println!("overflowing_add: 255 + 1 = {}, 溢出: {}", result, overflowed); // 0, true

let (result2, overflowed2) = 100u8.overflowing_add(50);
println!("overflowing_add: 100 + 50 = {}, 溢出: {}", result2, overflowed2); // 150, false

// 4. saturating_* 方法:饱和运算(停在最大/最小值)
let saturated = a.saturating_add(1);
println!("saturating_add: 255 + 1 = {}", saturated); // 255(停在最大值)

let saturated2 = 0u8.saturating_sub(10);
println!("saturating_sub: 0 - 10 = {}", saturated2); // 0(停在最小值)

let saturated3 = a.saturating_add(100);
println!("saturating_add: 255 + 100 = {}", saturated3); // 255
}

4.4 浮点类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
fn main() {
// 默认浮点类型是 f64
let x = 2.0; // f64(默认)
let y: f32 = 3.0; // f32,需要显式标注

// f64: 64位双精度浮点数(IEEE 754)
// f32: 32位单精度浮点数(IEEE 754)

println!("f64: {}", x);
println!("f32: {}", y);
println!("f64 大小: {} 字节", std::mem::size_of::<f64>());
println!("f32 大小: {} 字节", std::mem::size_of::<f32>());

// 浮点运算
let sum = 5.0 + 10.0;
let difference = 95.5 - 4.3;
let product = 4.0 * 30.0;
let quotient = 56.7 / 32.2;
let remainder = 43.0_f64 % 5.0;

println!("\n浮点运算:");
println!("加法: 5.0 + 10.0 = {}", sum);
println!("减法: 95.5 - 4.3 = {}", difference);
println!("乘法: 4.0 * 30.0 = {}", product);
println!("除法: 56.7 / 32.2 = {}", quotient);
println!("取余: 43.0 % 5.0 = {}", remainder);

// 浮点数的特殊值
let infinity = f64::INFINITY;
let neg_infinity = f64::NEG_INFINITY;
let nan = f64::NAN;

println!("\n特殊值:");
println!("正无穷: {}", infinity);
println!("负无穷: {}", neg_infinity);
println!("NaN: {}", nan);
println!("0.0 / 0.0 = {}", 0.0_f64 / 0.0); // NaN
println!("1.0 / 0.0 = {}", 1.0_f64 / 0.0); // inf

// NaN 不等于任何值(包括自身)
let nan_val = f64::NAN;
println!("\nNaN == NaN: {}", nan_val == nan_val); // false
println!("NaN 检测: {}", nan_val.is_nan()); // true

// 浮点数精度问题
println!("\n浮点精度:");
println!("0.1 + 0.2 = {}", 0.1 + 0.2); // 0.30000000000000004
println!("0.1 + 0.2 == 0.3: {}", (0.1_f64 + 0.2) == 0.3); // false

// 正确比较浮点数的方式
let a = 0.1_f64 + 0.2;
let b = 0.3_f64;
let epsilon = f64::EPSILON;
println!("使用 epsilon 比较: {}", (a - b).abs() < epsilon); // true

// 常用数学方法
println!("\n常用数学方法:");
println!("sqrt(2.0) = {}", 2.0_f64.sqrt());
println!("2.0^10 = {}", 2.0_f64.powi(10));
println!("2.0^1.5 = {}", 2.0_f64.powf(1.5));
println!("abs(-5.3) = {}", (-5.3_f64).abs());
println!("ceil(4.1) = {}", 4.1_f64.ceil());
println!("floor(4.9) = {}", 4.9_f64.floor());
println!("round(4.5) = {}", 4.5_f64.round());
println!("sin(PI/2) = {}", (std::f64::consts::PI / 2.0).sin());
println!("ln(E) = {}", std::f64::consts::E.ln());
}

4.5 布尔类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
fn main() {
let t = true;
let f: bool = false; // 显式类型标注

println!("true: {}, false: {}", t, f);
println!("bool 大小: {} 字节", std::mem::size_of::<bool>());

// 布尔运算
println!("\n布尔运算:");
println!("true && false = {}", true && false); // false
println!("true || false = {}", true || false); // true
println!("!true = {}", !true); // false

// 布尔值常用于条件判断
let number = 42;
let is_even = number % 2 == 0;
let is_positive = number > 0;

if is_even && is_positive {
println!("{} 是正偶数", number);
}

// 布尔值可以转换为整数
println!("\n布尔转整数:");
println!("true as i32 = {}", true as i32); // 1
println!("false as i32 = {}", false as i32); // 0
}

4.6 字符类型

Rust 的 char 类型是 4 字节的 Unicode 标量值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
fn main() {
let c = 'z';
let z: char = 'Z';
let heart = '❤';
let chinese = '中';
let emoji = '🦀'; // Rust 吉祥物螃蟹 Ferris
let japanese = 'あ';

println!("字符: {} {} {} {} {} {}", c, z, heart, chinese, emoji, japanese);
println!("char 大小: {} 字节", std::mem::size_of::<char>()); // 4 字节

// char 表示 Unicode 标量值(U+0000 到 U+D7FF 和 U+E000 到 U+10FFFF)
println!("\nUnicode 码点:");
println!("'A' = U+{:04X}", 'A' as u32); // U+0041
println!("'中' = U+{:04X}", '中' as u32); // U+4E2D
println!("'🦀' = U+{:04X}", '🦀' as u32); // U+1F980

// char 的常用方法
println!("\nchar 方法:");
println!("'A'.is_alphabetic() = {}", 'A'.is_alphabetic()); // true
println!("'3'.is_numeric() = {}", '3'.is_numeric()); // true
println!("'a'.is_lowercase() = {}", 'a'.is_lowercase()); // true
println!("'A'.is_uppercase() = {}", 'A'.is_uppercase()); // true
println!("' '.is_whitespace() = {}", ' '.is_whitespace()); // true
println!("'a'.to_uppercase() = {}", 'a'.to_uppercase()); // A
println!("'A'.to_lowercase() = {}", 'A'.to_lowercase()); // a
println!("'5'.is_ascii_digit() = {}", '5'.is_ascii_digit()); // true

// 注意:char 与字符串中的字符编码不同
// char 始终是 4 字节
// String/&str 使用 UTF-8 编码,每个字符 1-4 字节
println!("\n编码大小对比:");
println!("char 'A' 占 {} 字节", std::mem::size_of::<char>()); // 4
println!("字符串 \"A\" 占 {} 字节", "A".len()); // 1 (UTF-8)
println!("字符串 \"中\" 占 {} 字节", "中".len()); // 3 (UTF-8)
println!("字符串 \"🦀\" 占 {} 字节", "🦀".len()); // 4 (UTF-8)
}

5. 复合类型

5.1 元组(Tuple)

元组是将多个不同类型的值组合在一起的复合类型。元组长度固定,声明后不能改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
fn main() {
// ========== 声明元组 ==========
let tup: (i32, f64, u8) = (500, 6.4, 1);
println!("元组: {:?}", tup);

// 类型推断
let tup2 = (1, 2.0, "hello", true);
println!("推断类型的元组: {:?}", tup2);

// ========== 访问元组元素(通过点号 + 索引) ==========
let x = tup.0; // 500
let y = tup.1; // 6.4
let z = tup.2; // 1
println!("元组元素: x={}, y={}, z={}", x, y, z);

// ========== 解构元组 ==========
let (a, b, c) = tup;
println!("解构: a={}, b={}, c={}", a, b, c);

// 部分解构(使用 _ 忽略不需要的值)
let (first, _, last) = tup;
println!("部分解构: first={}, last={}", first, last);

// ========== 单元元组(unit) ==========
// 空元组 () 称为"单元类型"(unit type)
// 不返回值的函数隐式返回 ()
let unit: () = ();
println!("单元类型: {:?}", unit);
println!("单元类型大小: {} 字节", std::mem::size_of::<()>()); // 0

// ========== 可变元组 ==========
let mut mutable_tup = (1, 2, 3);
println!("修改前: {:?}", mutable_tup);
mutable_tup.0 = 10;
mutable_tup.2 = 30;
println!("修改后: {:?}", mutable_tup);

// ========== 元组作为函数返回值 ==========
let (area, perimeter) = calculate_rect(5.0, 3.0);
println!("矩形 5x3: 面积={}, 周长={}", area, perimeter);

// ========== 嵌套元组 ==========
let nested = ((1, 2), (3, 4));
println!("嵌套元组: ({}, {})", (nested.0).0, (nested.1).1);
}

fn calculate_rect(width: f64, height: f64) -> (f64, f64) {
let area = width * height;
let perimeter = 2.0 * (width + height);
(area, perimeter)
}

5.2 数组(Array)

数组是固定长度的同类型元素集合,存储在栈上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
fn main() {
// ========== 声明数组 ==========
let arr = [1, 2, 3, 4, 5];
println!("数组: {:?}", arr);

// 显式类型标注: [类型; 长度]
let arr2: [i32; 5] = [1, 2, 3, 4, 5];
println!("显式类型: {:?}", arr2);

// 初始化为相同值: [初始值; 长度]
let zeros = [0; 10]; // 10 个 0
let ones = [1.0_f64; 5]; // 5 个 1.0
println!("全零数组: {:?}", zeros);
println!("全一数组: {:?}", ones);

// ========== 访问数组元素 ==========
let first = arr[0]; // 1(索引从 0 开始)
let second = arr[1]; // 2
let last = arr[4]; // 5
println!("第一个: {}, 第二个: {}, 最后一个: {}", first, second, last);

// ========== 数组长度 ==========
println!("数组长度: {}", arr.len());
println!("数组大小: {} 字节", std::mem::size_of_val(&arr));

// ========== 越界检查 ==========
// Rust 在运行时检查数组越界,防止缓冲区溢出
// let invalid = arr[10]; // 运行时 panic: index out of bounds

// 安全访问:使用 get 方法返回 Option
match arr.get(2) {
Some(val) => println!("arr[2] = {}", val),
None => println!("索引越界!"),
}
match arr.get(10) {
Some(val) => println!("arr[10] = {}", val),
None => println!("arr[10] 索引越界!"),
}

// ========== 可变数组 ==========
let mut mut_arr = [1, 2, 3, 4, 5];
println!("修改前: {:?}", mut_arr);
mut_arr[0] = 10;
mut_arr[4] = 50;
println!("修改后: {:?}", mut_arr);

// ========== 遍历数组 ==========
println!("\n遍历数组:");
for element in &arr {
print!("{} ", element);
}
println!();

// 带索引遍历
for (index, value) in arr.iter().enumerate() {
println!(" arr[{}] = {}", index, value);
}

// ========== 数组常用方法 ==========
let numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3];
println!("\n数组方法:");
println!("包含 5: {}", numbers.contains(&5));
println!("以 [3,1] 开头: {}", numbers.starts_with(&[3, 1]));

// 排序需要可变数组
let mut sortable = [3, 1, 4, 1, 5, 9, 2, 6];
sortable.sort();
println!("排序后: {:?}", sortable);

// ========== 多维数组 ==========
let matrix: [[i32; 3]; 2] = [
[1, 2, 3],
[4, 5, 6],
];
println!("\n二维数组:");
for row in &matrix {
println!(" {:?}", row);
}
println!("matrix[1][2] = {}", matrix[1][2]); // 6
}

6. 类型推断与类型标注

6.1 类型推断

Rust 编译器通常能自动推断变量类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
// 编译器自动推断类型
let x = 5; // i32(默认整数类型)
let y = 2.0; // f64(默认浮点类型)
let z = true; // bool
let s = "hello"; // &str
let s2 = String::from("hello"); // String

// 通过使用上下文推断类型
let mut numbers = Vec::new(); // 此时 Vec 的元素类型未知
numbers.push(5); // 现在编译器推断为 Vec<i32>
println!("numbers: {:?}", numbers);

// 通过后续操作推断
let guess = "42".parse().expect("不是数字");
let _result: i32 = guess; // 从这里推断 guess 是 i32

// 也可以用 turbofish 语法指定类型
let guess2 = "42".parse::<i32>().expect("不是数字");
println!("guess2: {}", guess2);
}

6.2 类型标注

有时编译器无法推断类型,需要显式标注。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fn main() {
// 必须标注类型的场景

// 1. parse 等方法需要知道目标类型
let number: i64 = "42".parse().unwrap();
// 或使用 turbofish
let number2 = "42".parse::<f64>().unwrap();
println!("i64: {}, f64: {}", number, number2);

// 2. 集合类型
let empty_vec: Vec<i32> = Vec::new();
let empty_map: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
println!("空 Vec: {:?}, 空 Map: {:?}", empty_vec, empty_map);

// 3. 需要特定精度的数值
let precise: f64 = 1.0;
let less_precise: f32 = 1.0;
println!("f64: {}, f32: {}", precise, less_precise);

// 4. 函数参数和返回值必须标注类型
fn add(a: i32, b: i32) -> i32 {
a + b
}
println!("add(3, 4) = {}", add(3, 4));
}

7. 类型转换

7.1 as 关键字

as 用于基本类型之间的转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
fn main() {
// ========== 整数之间转换 ==========
let x: i32 = 42;
let y: i64 = x as i64; // 小转大,安全
let z: i8 = x as i8; // 大转小,可能截断
println!("i32({}) -> i64({}), i8({})", x, y, z);

// 溢出时会截断
let big: i32 = 300;
let small: u8 = big as u8; // 300 % 256 = 44
println!("i32({}) -> u8({})", big, small); // 44

// ========== 浮点与整数转换 ==========
let f = 3.99_f64;
let i = f as i32; // 截断小数部分(不是四舍五入)
println!("f64({}) -> i32({})", f, i); // 3

let i2 = 42_i32;
let f2 = i2 as f64;
println!("i32({}) -> f64({})", i2, f2);

// 负浮点数转无符号整数
let negative = -1.0_f64;
let unsigned = negative as u32;
println!("f64({}) -> u32({})", negative, unsigned); // 0(饱和转换)

// ========== char 与整数转换 ==========
let c = 'A';
let code = c as u32;
println!("'{}' 的 Unicode 码点: {}", c, code); // 65

let code2 = 97u8;
let c2 = code2 as char;
println!("码点 {} 对应字符: '{}'", code2, c2); // 'a'

// ========== bool 与整数转换 ==========
println!("true as i32 = {}", true as i32); // 1
println!("false as i32 = {}", false as i32); // 0
// 注意:不能将整数直接 as 转为 bool

// ========== 浮点精度转换 ==========
let f64_val: f64 = 3.141592653589793;
let f32_val = f64_val as f32;
println!("f64({}) -> f32({})", f64_val, f32_val);
}

7.2 其他转换方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
fn main() {
// From / Into trait
let s = String::from("hello"); // &str -> String
let num: i64 = i64::from(42_i32); // i32 -> i64
println!("String: {}, i64: {}", s, num);

// Into 是 From 的逆向
let num2: i64 = 42_i32.into();
println!("into: {}", num2);

// TryFrom / TryInto(可能失败的转换)
use std::convert::TryFrom;
use std::convert::TryInto;

let big_number: i32 = 300;
let result: Result<u8, _> = u8::try_from(big_number);
match result {
Ok(val) => println!("转换成功: {}", val),
Err(e) => println!("转换失败: {}", e), // 这里会失败
}

let small_number: i32 = 100;
let result2: Result<u8, _> = small_number.try_into();
match result2 {
Ok(val) => println!("转换成功: {}", val), // 100
Err(e) => println!("转换失败: {}", e),
}

// ToString / Display
let n = 42;
let s = n.to_string();
println!("数字转字符串: \"{}\"", s);

// 字符串转数字
let parsed: i32 = "42".parse().unwrap();
let parsed2: f64 = "3.14".parse().unwrap();
println!("字符串转数字: i32={}, f64={}", parsed, parsed2);
}

8. 表达式与语句

Rust 是一门基于表达式的语言。理解表达式与语句的区别非常重要。

8.1 语句(Statement)

语句执行操作但不返回值

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
// let 绑定是语句
let x = 5; // 语句,不返回值

// 因此不能这样写:
// let y = (let x = 5); // 编译错误!let 是语句,没有返回值

// 函数定义也是语句
fn add(a: i32, b: i32) -> i32 { a + b }

println!("x = {}, add(1,2) = {}", x, add(1, 2));
}

8.2 表达式(Expression)

表达式计算并返回一个值。Rust 中几乎所有东西都是表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
fn main() {
// 数字字面量是表达式
let x = 5; // 5 是表达式,返回值 5

// 算术运算是表达式
let y = 5 + 3; // 5 + 3 是表达式,返回值 8

// 函数调用是表达式
let z = add(1, 2); // add(1, 2) 是表达式,返回值 3

// 代码块(花括号)是表达式
let block_value = {
let a = 10;
let b = 20;
a + b // 注意:没有分号!这是代码块的返回值
};
println!("代码块的值: {}", block_value); // 30

// if 是表达式
let condition = true;
let if_value = if condition { 5 } else { 10 };
println!("if 表达式的值: {}", if_value); // 5

// match 是表达式
let number = 3;
let match_value = match number {
1 => "一",
2 => "二",
3 => "三",
_ => "其他",
};
println!("match 表达式的值: {}", match_value); // 三

println!("x={}, y={}, z={}", x, y, z);
}

fn add(a: i32, b: i32) -> i32 {
a + b // 表达式作为返回值(没有 return,没有分号)
}

8.3 分号的重要性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn main() {
// 有分号:变成语句,返回 ()
let a = {
let x = 5;
x + 1; // 注意分号!这使得代码块返回 ()
};
println!("有分号: {:?}", a); // ()

// 无分号:是表达式,返回值
let b = {
let x = 5;
x + 1 // 没有分号!这是代码块的返回值
};
println!("无分号: {:?}", b); // 6
}

9. 注释

9.1 行注释

1
2
3
4
5
6
7
8
9
10
11
fn main() {
// 这是行注释
let x = 5; // 行尾注释

// 可以连续使用多行注释
// 来解释较复杂的逻辑
// 每行都需要 //
let y = x + 1;

println!("x={}, y={}", x, y);
}

9.2 块注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fn main() {
/* 这是块注释 */

/*
* 多行块注释
* 可以跨越多行
* 星号不是必需的,只是惯例
*/

let x = /* 内联块注释 */ 42;

/*
块注释可以嵌套(与 C/C++ 不同!)
/* 这是嵌套的注释 */
嵌套注释在 Rust 中是合法的
*/

println!("x = {}", x);
}

9.3 文档注释(///)

/// 用于为紧随其后的项生成文档。支持 Markdown 格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/// 计算两个数的和
///
/// # 参数
///
/// * `a` - 第一个加数
/// * `b` - 第二个加数
///
/// # 返回值
///
/// 返回 `a` 和 `b` 的和
///
/// # 示例
///
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
///
/// # Panics
///
/// 此函数不会 panic。
fn add(a: i32, b: i32) -> i32 {
a + b
}

/// 表示一个二维点
///
/// # 示例
///
/// ```
/// let p = Point { x: 1.0, y: 2.0 };
/// println!("点坐标: ({}, {})", p.x, p.y);
/// ```
struct Point {
/// x 坐标
x: f64,
/// y 坐标
y: f64,
}

fn main() {
let result = add(3, 4);
println!("3 + 4 = {}", result);

let p = Point { x: 1.0, y: 2.0 };
println!("点: ({}, {})", p.x, p.y);
}

9.4 模块文档注释(//!)

//! 用于为包含它的项(通常是模块或 crate)生成文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//! # 我的数学库
//!
//! `my_math` 提供了常用的数学计算函数。
//!
//! ## 功能
//!
//! - 基本算术运算
//! - 几何计算
//!
//! ## 使用示例
//!
//! ```rust
//! use my_math::add;
//! let result = add(2, 3);
//! ```

/// 计算两个整数的和
fn add(a: i32, b: i32) -> i32 {
a + b
}

fn main() {
println!("2 + 3 = {}", add(2, 3));

// 生成文档命令:
// cargo doc --open
}

10. println! 宏的格式化

println! 是 Rust 最常用的输出宏,支持丰富的格式化选项。

10.1 基本输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
// 基本输出
println!("Hello, World!");

// 位置参数
println!("{} {} {}", "Hello", "Rust", 2024);

// 索引参数
println!("{0} 喜欢 {1},{1} 也喜欢 {0}", "Alice", "Bob");

// 命名参数
println!("{name} 今年 {age} 岁", name = "张三", age = 25);

// 变量捕获(Rust 1.58+)
let name = "李四";
let age = 30;
println!("{name} 今年 {age} 岁"); // 直接在格式字符串中使用变量名
}

10.2 Display 格式化({})

1
2
3
4
5
6
7
8
fn main() {
// {} 使用 Display trait 格式化
println!("整数: {}", 42);
println!("浮点: {}", 3.14);
println!("字符串: {}", "hello");
println!("布尔: {}", true);
println!("字符: {}", 'A');
}

10.3 Debug 格式化({:?} 和 {:#?})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
fn main() {
// {:?} 使用 Debug trait 格式化
let vec = vec![1, 2, 3];
let tuple = (1, "hello", true);
let array = [1, 2, 3, 4, 5];

println!("Debug 格式:");
println!("Vec: {:?}", vec);
println!("Tuple: {:?}", tuple);
println!("Array: {:?}", array);

// {:#?} 美化的 Debug 输出(pretty print)
let nested = vec![vec![1, 2], vec![3, 4], vec![5, 6]];
println!("\n普通 Debug:");
println!("{:?}", nested);

println!("\n美化 Debug:");
println!("{:#?}", nested);

// 自定义结构体需要 derive Debug
#[derive(Debug)]
struct Person {
name: String,
age: u32,
hobbies: Vec<String>,
}

let person = Person {
name: String::from("张三"),
age: 25,
hobbies: vec![String::from("编程"), String::from("阅读")],
};

println!("\n结构体 Debug:");
println!("{:?}", person);
println!("\n结构体美化 Debug:");
println!("{:#?}", person);
}

10.4 数字格式化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
fn main() {
let n = 42;
let f = 3.14159265;

// ========== 进制格式化 ==========
println!("===== 进制格式化 =====");
println!("十进制: {}", n); // 42
println!("二进制: {:b}", n); // 101010
println!("八进制: {:o}", n); // 52
println!("十六进制(小写): {:x}", n); // 2a
println!("十六进制(大写): {:X}", n); // 2A
println!("带前缀的二进制: {:#b}", n); // 0b101010
println!("带前缀的八进制: {:#o}", n); // 0o52
println!("带前缀的十六进制: {:#x}", n); // 0x2a

// ========== 浮点精度 ==========
println!("\n===== 浮点精度 =====");
println!("默认: {}", f); // 3.14159265
println!("2位小数: {:.2}", f); // 3.14
println!("0位小数: {:.0}", f); // 3
println!("5位小数: {:.5}", f); // 3.14159
println!("科学计数法(小写): {:e}", f); // 3.14159265e0
println!("科学计数法(大写): {:E}", f); // 3.14159265E0

// ========== 宽度与对齐 ==========
println!("\n===== 宽度与对齐 =====");
println!("右对齐(10): '{:>10}'", "hello"); // ' hello'
println!("左对齐(10): '{:<10}'", "hello"); // 'hello '
println!("居中(10): '{:^10}'", "hello"); // ' hello '

// 使用填充字符
println!("用 * 右对齐: '{:*>10}'", "hello"); // '*****hello'
println!("用 - 左对齐: '{:-<10}'", "hello"); // 'hello-----'
println!("用 = 居中: '{:=^10}'", "hello"); // '==hello==='

// 数字宽度
println!("数字右对齐: '{:>10}'", 42); // ' 42'
println!("数字前补零: '{:0>10}'", 42); // '0000000042'
println!("数字前补零(简写): '{:010}'", 42); // '0000000042'

// ========== 正负号 ==========
println!("\n===== 正负号 =====");
println!("默认: {} {}", 42, -42); // 42 -42
println!("强制显示正号: {:+} {:+}", 42, -42); // +42 -42

// ========== 组合使用 ==========
println!("\n===== 组合使用 =====");
println!("宽度+精度: '{:>10.2}'", 3.14159); // ' 3.14'
println!("填充+精度: '{:0>10.2}'", 3.14159); // '0000003.14'
println!("正号+精度: '{:+.3}'", 3.14159); // '+3.142'

// ========== 动态宽度和精度 ==========
println!("\n===== 动态宽度和精度 =====");
let width = 15;
let precision = 3;
println!("动态宽度: '{:>width$}'", "hello");
println!("动态精度: '{:.precision$}'", 3.14159);
println!("都动态: '{:>width$.precision$}'", 3.14159);
}

10.5 其他输出宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
fn main() {
// print! - 不换行输出
print!("Hello ");
print!("World");
println!(); // 换行

// eprint! / eprintln! - 输出到标准错误
eprintln!("这是一条错误信息");

// format! - 格式化为 String,不输出
let s = format!("{} + {} = {}", 1, 2, 3);
println!("格式化字符串: {}", s);

// dbg! - 调试宏,输出表达式及其值到 stderr
let x = 5;
let y = dbg!(x * 2); // 输出: [src/main.rs:行号] x * 2 = 10
println!("y = {}", y);

// dbg! 可以嵌套
let a = 2;
let b = 3;
let c = dbg!(dbg!(a) + dbg!(b));
println!("c = {}", c);

// write! / writeln! - 写入到实现了 Write trait 的对象
use std::fmt::Write;
let mut buf = String::new();
writeln!(buf, "第一行").unwrap();
writeln!(buf, "第二行: {}", 42).unwrap();
println!("缓冲区内容:\n{}", buf);
}

10.6 转义字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fn main() {
// 常用转义字符
println!("换行符: 第一行\n第二行");
println!("制表符: 列1\t列2\t列3");
println!("反斜杠: \\");
println!("双引号: \"hello\"");
println!("单引号: \'");
println!("空字符: \\0");

// Unicode 转义
println!("Unicode: \\u{{4e2d}} = \u{4e2d}"); // 中
println!("Unicode: \\u{{1f980}} = \u{1f980}"); // 🦀

// ASCII 转义
println!("ASCII: \\x41 = \x41"); // A

// 原始字符串(不处理转义)
println!(r"原始字符串: \n \t \\ 都不会被转义");
println!(r#"可以包含 "双引号" 的原始字符串"#);
println!(r##"甚至可以包含 "# 的原始字符串"##);

// 字节字符串
let bytes = b"hello";
println!("字节字符串: {:?}", bytes); // [104, 101, 108, 108, 111]
}

总结

本章我们学习了 Rust 的基础语法与数据类型:

  1. 变量与可变性:默认不可变,使用 mut 使其可变
  2. 变量遮蔽:可以在同一作用域重新声明同名变量,甚至改变类型
  3. 常量与静态变量const 编译期内联,static 有固定地址
  4. 基本数据类型:整数、浮点数、布尔、字符
  5. 复合类型:元组(不同类型组合)和数组(同类型固定长度)
  6. 类型系统:类型推断、类型标注、as 转换
  7. 表达式与语句:Rust 是基于表达式的语言
  8. 注释:行注释、块注释、文档注释
  9. 格式化输出:丰富的 println! 格式化选项

下一章我们将深入学习 Rust 最核心的概念 – 所有权与借用。