第三章 Rust 基础语法速览#

"A language that doesn't affect the way you think about programming is not worth knowing." — Alan Perlis

本章将带你快速掌握 Rust 的基础语法。如果你有 C++ 或 Java 背景,你会发现很多概念似曾相识,但 Rust 在细节上做了许多独到的设计。我们会在每个知识点上与 C++/Java 做对照,帮助你快速建立映射关系。

        mindmap
  root((Rust 初印象))
    工具链
      rustup
      cargo
      crates.io
    基础语法
      let 和 mut
      表达式
      函数
    类型系统
      标量
      复合类型
      推断
    工程直觉
      编译器反馈
      格式化
      包管理
    

3.1 变量与可变性#

3.1.1 默认不可变#

Rust 中的变量默认是不可变的(immutable)。这与 C++ 的 const 和 Java 的 final 不同——在 Rust 中,不可变是默认行为。

fn main() {
    let x = 5;       // 不可变变量
    // x = 6;         // ❌ 编译错误!cannot assign twice to immutable variable
    
    let mut y = 5;   // 可变变量(需要显式声明 mut)
    y = 6;           // ✓ OK
    println!("x = {}, y = {}", x, y);
}

三语对照:

概念

Rust

C++

Java

不可变变量

let x = 5;

const int x = 5;

final int x = 5;

可变变量

let mut x = 5;

int x = 5;

int x = 5;

设计哲学: Rust 选择"默认不可变"是因为不可变数据更容易推理、更安全(尤其在并发场景下)。如果你需要可变,必须显式声明 mut——这迫使你思考"这个变量真的需要被修改吗?"

3.1.2 变量遮蔽(Shadowing)#

Rust 允许用 let 重新声明同名变量,这叫做遮蔽(shadowing)。被遮蔽的变量不会被销毁,只是在当前作用域中不可访问。

fn main() {
    let x = 5;
    let x = x + 1;       // 遮蔽:x 现在是 6
    let x = x * 2;       // 再次遮蔽:x 现在是 12
    println!("x = {}", x); // 输出: x = 12

    // 遮蔽甚至可以改变类型!
    let spaces = "   ";          // &str 类型
    let spaces = spaces.len();   // usize 类型
    println!("spaces = {}", spaces); // 输出: spaces = 3
}

遮蔽 vs mut 的区别:

// 遮蔽:可以改变类型
let x = "hello";
let x = x.len();    // ✓ OK,x 从 &str 变成 usize

// mut:不能改变类型
let mut y = "hello";
// y = y.len();      // ❌ 编译错误!类型不匹配

C++ 工程师注意: C++ 中在同一作用域内不能重新声明同名变量,但 Rust 可以。这在需要对同一个值做多步转换时非常方便。

3.1.3 常量#

常量(const)与不可变变量不同:常量在编译期求值,必须标注类型,且命名约定为全大写蛇形。

const MAX_CONNECTIONS: u32 = 10_000;  // 编译期常量
const PI: f64 = 3.141_592_653_589_793;

fn main() {
    println!("最大连接数: {}", MAX_CONNECTIONS);
}

特性

let 变量

const 常量

static 静态变量

可变性

默认不可变,可 mut

永远不可变

mut(需 unsafe)

类型推导

支持

不支持(必须标注)

不支持(必须标注)

求值时机

运行时

编译期

运行时(程序启动时)

内存

栈上

内联到使用处

固定地址('static 生命周期)


3.2 数据类型#

3.2.1 标量类型#

Rust 有四种标量类型:整数、浮点数、布尔值和字符。

整数类型:

┌──────────┬─────────┬──────────┬──────────────────────────┐
│  长度     │ 有符号   │ 无符号   │ 范围                      │
├──────────┼─────────┼──────────┼──────────────────────────┤
│  8-bit   │ i8      │ u8       │ -128~127 / 0~255         │
│  16-bit  │ i16     │ u16      │ -32768~32767 / 0~65535   │
│  32-bit  │ i32     │ u32      │ -2³¹~2³¹-1 / 0~2³²-1    │
│  64-bit  │ i64     │ u64      │ -2⁶³~2⁶³-1 / 0~2⁶⁴-1    │
│  128-bit │ i128    │ u128     │ 非常大                    │
│  arch    │ isize   │ usize    │ 取决于平台 (32/64位)      │
└──────────┴─────────┴──────────┴──────────────────────────┘
fn main() {
    let a: i32 = 42;          // 默认整数类型
    let b: u8 = 255;          // 无符号 8 位
    let c: isize = -100;      // 平台相关大小
    let d = 1_000_000;        // 下划线分隔,提高可读性
    let e = 0xff;             // 十六进制
    let f = 0o77;             // 八进制
    let g = 0b1111_0000;      // 二进制
    let h = b'A';             // 字节字面量(u8)
    
    println!("a={}, b={}, c={}, d={}", a, b, c, d);
}

与 C++ 对比: C++ 的 int 大小取决于平台(通常 32 位),Rust 的 i32 永远是 32 位。Rust 没有隐式整数转换——你必须显式使用 as 进行类型转换。

浮点数类型:

fn main() {
    let x = 2.0;      // f64(默认)
    let y: f32 = 3.0;  // 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 % 5.0;
    
    println!("{} {} {} {} {}", sum, difference, product, quotient, remainder);
}

布尔类型:

fn main() {
    let t = true;
    let f: bool = false;
    println!("t={}, f={}", t, f);
}

字符类型:

fn main() {
    let c = 'z';
    let z: char = 'ℤ';
    let heart_eyed_cat = '😻';
    
    // Rust 的 char 是 4 字节的 Unicode 标量值!
    println!("字符大小: {} 字节", std::mem::size_of::<char>()); // 输出: 4
}

重要区别: Rust 的 char 是 4 字节 Unicode 标量值,不是 C++ 的 1 字节 char,也不是 Java 的 2 字节 UTF-16 char

3.2.2 复合类型#

元组(Tuple):

fn main() {
    // 元组:固定长度,可以包含不同类型
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    
    // 解构
    let (x, y, z) = tup;
    println!("x={}, y={}, z={}", x, y, z);
    
    // 索引访问
    let five_hundred = tup.0;
    let six_point_four = tup.1;
    let one = tup.2;
    println!("{} {} {}", five_hundred, six_point_four, one);
    
    // 单元元组(unit type)
    let unit: () = ();  // 类似 C++ 的 void
}

数组(Array):

fn main() {
    // 数组:固定长度,相同类型,栈上分配
    let a = [1, 2, 3, 4, 5];
    let b: [i32; 5] = [1, 2, 3, 4, 5];   // 显式类型标注
    let c = [3; 5];                        // [3, 3, 3, 3, 3]
    
    // 索引访问
    let first = a[0];
    let second = a[1];
    println!("first={}, second={}", first, second);
    
    // ⚠️ 越界访问会 panic(不是未定义行为!)
    // let oob = a[10]; // 运行时 panic: index out of bounds
}

三语对照 — 复合类型:

概念

Rust

C++

Java

固定数组

[i32; 5]

int[5] / std::array<int,5>

int[]

动态数组

Vec<i32>

std::vector<int>

ArrayList<Integer>

元组

(i32, f64)

std::tuple<int, double>

无(需自定义类)

字符串(拥有)

String

std::string

String

字符串(借用)

&str

std::string_view

无直接对应


3.3 函数#

3.3.1 函数定义#

Rust 使用 fn 关键字定义函数,参数必须标注类型:

fn add(a: i32, b: i32) -> i32 {
    a + b  // 注意:没有分号!这是一个表达式,作为返回值
}

fn greet(name: &str) {
    println!("Hello, {}!", name);
}

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

3.3.2 语句与表达式#

Rust 是一门表达式导向的语言。理解语句(statement)和表达式(expression)的区别至关重要:

fn main() {
    // 语句(statement):执行操作,不返回值
    let x = 5;           // let 绑定是语句
    // let y = (let z = 6); // ❌ 编译错误!语句不返回值

    // 表达式(expression):求值并返回结果
    let y = {
        let x = 3;
        x + 1        // 没有分号 → 这是表达式,值为 4
    };
    println!("y = {}", y); // 输出: y = 4

    // 加了分号就变成语句,返回 ()
    let z = {
        let x = 3;
        x + 1;       // 有分号 → 这是语句,块返回 ()
    };
    // z 的类型是 ()
}

关键规则: 函数体最后一个表达式(没有分号)就是返回值。加了分号就变成语句,返回 ()。这是 Rust 新手最常犯的错误之一。

// ✓ 正确:最后一个表达式作为返回值
fn add_v1(a: i32, b: i32) -> i32 {
    a + b
}

// ✓ 正确:使用 return 关键字(通常用于提前返回)
fn add_v2(a: i32, b: i32) -> i32 {
    return a + b;
}

// ❌ 错误:加了分号,返回 () 而不是 i32
// fn add_v3(a: i32, b: i32) -> i32 {
//     a + b;  // 编译错误!expected `i32`, found `()`
// }

3.3.3 闭包(Closure)#

闭包是可以捕获环境变量的匿名函数:

fn main() {
    // 基本闭包
    let add = |a, b| a + b;
    println!("3 + 5 = {}", add(3, 5));

    // 带类型标注的闭包
    let multiply = |a: i32, b: i32| -> i32 { a * b };
    println!("3 * 5 = {}", multiply(3, 5));

    // 捕获环境变量
    let name = String::from("Walter");
    let greet = || println!("Hello, {}!", name);  // 借用 name
    greet();
    println!("name is still: {}", name);  // name 仍然可用

    // move 闭包:获取所有权
    let name2 = String::from("Rust");
    let greet2 = move || println!("Hello, {}!", name2);  // 移动 name2
    greet2();
    // println!("{}", name2); // ❌ 编译错误!name2 已被移动
}

三语对照 — 闭包/Lambda:

语言

语法

捕获方式

Rust

|a, b| a + b

自动推导(借用/可变借用/移动)

C++

[&](int a, int b) { return a + b; }

显式指定 [=]/[&]/[x]

Java

(a, b) -> a + b

只能捕获 effectively final 变量


3.4 控制流#

3.4.1 if 表达式#

在 Rust 中,if 是一个表达式,可以返回值:

fn main() {
    let number = 7;

    // 基本 if-else
    if number < 5 {
        println!("小于 5");
    } else if number < 10 {
        println!("小于 10");
    } else {
        println!("大于等于 10");
    }

    // if 作为表达式(类似三元运算符)
    let condition = true;
    let x = if condition { 5 } else { 6 };
    println!("x = {}", x); // 输出: x = 5

    // ⚠️ 条件不需要括号,且必须是 bool 类型
    // if number { ... }  // ❌ 编译错误!Rust 没有"truthy"值
    if number != 0 {
        println!("number is not zero");
    }
}

与 C++/Java 的区别: Rust 的 if 条件不需要括号,且必须是 bool 类型。不存在 C++ 中 if (ptr) 或 Java 中 if (obj != null) 的隐式转换。

3.4.2 循环#

Rust 有三种循环:loopwhilefor

loop — 无限循环:

fn main() {
    let mut counter = 0;

    // loop 可以返回值!
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2;  // break 带返回值
        }
    };
    println!("result = {}", result); // 输出: result = 20

    // 循环标签(用于嵌套循环的 break/continue)
    let mut count = 0;
    'outer: loop {
        let mut remaining = 10;
        loop {
            if remaining == 9 {
                break;           // 只退出内层循环
            }
            if count == 2 {
                break 'outer;   // 退出外层循环
            }
            remaining -= 1;
        }
        count += 1;
    }
    println!("count = {}", count); // 输出: count = 2
}

while 循环:

fn main() {
    let mut number = 3;
    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }
    println!("发射!");
}

for 循环(最常用):

fn main() {
    // 遍历数组
    let a = [10, 20, 30, 40, 50];
    for element in a {
        println!("值: {}", element);
    }

    // 带索引遍历
    for (i, element) in a.iter().enumerate() {
        println!("a[{}] = {}", i, element);
    }

    // 范围(Range)
    for number in 1..=5 {       // 1, 2, 3, 4, 5(包含终点)
        println!("{}", number);
    }

    for number in (1..4).rev() { // 3, 2, 1(反转,不包含终点)
        println!("{}", number);
    }
}

三语对照 — 循环:

概念

Rust

C++

Java

无限循环

loop { }

while(true) { }

while(true) { }

条件循环

while cond { }

while(cond) { }

while(cond) { }

范围遍历

for i in 0..n { }

for(int i=0;i<n;i++)

for(int i=0;i<n;i++)

容器遍历

for x in vec { }

for(auto& x : vec)

for(var x : list)

带索引遍历

for (i,x) in v.iter().enumerate()

需手动计数

需手动计数


3.5 模式匹配#

模式匹配是 Rust 最强大的特性之一。match 表达式类似于 C/C++ 的 switch,但功能远远更强。

3.5.1 基本 match#

fn main() {
    let number = 13;

    match number {
        1 => println!("一"),
        2 => println!("二"),
        3..=12 => println!("三到十二"),  // 范围匹配
        13 | 14 => println!("十三或十四"), // 多值匹配
        _ => println!("其他"),            // 通配符(类似 default)
    }
}

3.5.2 match 是表达式#

fn describe_number(n: i32) -> &'static str {
    match n {
        0 => "零",
        1..=9 => "个位数",
        10..=99 => "两位数",
        100..=999 => "三位数",
        _ => "大数",
    }
}

fn main() {
    println!("{} 是{}", 42, describe_number(42));
    // 输出: 42 是两位数
}

3.5.3 解构匹配#

fn main() {
    // 解构元组
    let point = (3, -5);
    match point {
        (0, 0) => println!("原点"),
        (x, 0) => println!("x 轴上,x={}", x),
        (0, y) => println!("y 轴上,y={}", y),
        (x, y) => println!("({}, {})", x, y),
    }

    // 解构结构体
    struct Point { x: i32, y: i32 }
    let p = Point { x: 0, y: 7 };
    match p {
        Point { x: 0, y } => println!("在 y 轴上,y={}", y),
        Point { x, y: 0 } => println!("在 x 轴上,x={}", x),
        Point { x, y } => println!("({}, {})", x, y),
    }
}

3.5.4 守卫条件(Match Guard)#

fn main() {
    let num = Some(4);

    match num {
        Some(x) if x < 5 => println!("小于 5: {}", x),
        Some(x) => println!("大于等于 5: {}", x),
        None => println!("无值"),
    }
}

3.5.5 if let 和 while let#

当你只关心一种模式时,if letmatch 更简洁:

fn main() {
    let config_max = Some(3u8);

    // 使用 match(有点啰嗦)
    match config_max {
        Some(max) => println!("最大值: {}", max),
        _ => (),
    }

    // 使用 if let(更简洁)
    if let Some(max) = config_max {
        println!("最大值: {}", max);
    }

    // while let
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() {
        println!("{}", top); // 输出: 3, 2, 1
    }
}

三语对照 — 模式匹配:

特性

Rust match

C++ switch

Java switch

穷尽性检查

✓(编译器强制)

部分(sealed class)

范围匹配

3..=12

不支持

不支持

解构

✓(元组/结构体/枚举)

部分(Java 21+)

守卫条件

if x > 5

不支持

when(Java 21+)

返回值

✓(表达式)

✓(Java 14+ switch 表达式)

Fall-through

✗(安全)

✓(需 break)

✓(需 break)


3.6 字符串#

字符串是 Rust 中最让新手困惑的部分之一。Rust 有两种主要的字符串类型:

3.6.1 String vs &str#

fn main() {
    // &str:字符串切片,不可变引用,通常指向静态内存或 String 内部
    let s1: &str = "hello";  // 字符串字面量,类型是 &str

    // String:堆分配的、可增长的字符串
    let s2: String = String::from("hello");
    let s3: String = "hello".to_string();  // 另一种创建方式
    let s4: String = format!("hello {}", "world");

    // String → &str(自动解引用)
    let s5: &str = &s2;

    // &str → String(需要显式转换)
    let s6: String = s1.to_string();
    let s7: String = String::from(s1);
}
┌─────────────────────────────────────────────┐
│              String vs &str                  │
│                                             │
│  &str "hello"                               │
│  ┌─────────┐                                │
│  │ ptr  ────┼──► [h][e][l][l][o]  (只读)    │
│  │ len: 5   │    (可能在静态内存/栈/堆)      │
│  └─────────┘                                │
│                                             │
│  String::from("hello")                      │
│  ┌─────────┐                                │
│  │ ptr  ────┼──► [h][e][l][l][o][  ][  ]    │
│  │ len: 5   │    (堆内存,可增长)            │
│  │ cap: 7   │                               │
│  └─────────┘                                │
└─────────────────────────────────────────────┘

3.6.2 字符串操作#

fn main() {
    let mut s = String::from("Hello");

    // 追加
    s.push(' ');           // 追加单个字符
    s.push_str("World");   // 追加字符串切片
    println!("{}", s);     // Hello World

    // 拼接
    let s1 = String::from("Hello, ");
    let s2 = String::from("World!");
    let s3 = s1 + &s2;    // 注意:s1 被移动了,s2 被借用
    // println!("{}", s1);  // ❌ s1 已被移动
    println!("{}", s3);    // Hello, World!

    // format! 宏(不会移动任何变量)
    let s4 = String::from("tic");
    let s5 = String::from("tac");
    let s6 = String::from("toe");
    let result = format!("{}-{}-{}", s4, s5, s6);
    println!("{}", result); // tic-tac-toe
    // s4, s5, s6 仍然可用

    // 遍历字符
    for c in "你好世界".chars() {
        println!("{}", c);
    }

    // 遍历字节
    for b in "hello".bytes() {
        println!("{}", b);
    }

    // ⚠️ 不能用索引访问!
    let s = String::from("hello");
    // let h = s[0]; // ❌ 编译错误!String 不支持索引
    let h = &s[0..1]; // ✓ 可以用切片(但要小心 UTF-8 边界)
    println!("第一个字节: {}", h);
}

为什么不能索引? Rust 的字符串是 UTF-8 编码的。一个中文字符占 3 个字节,s[0] 的语义不明确(是第一个字节还是第一个字符?)。Rust 选择不支持索引来避免这种歧义。

三语对照 — 字符串:

操作

Rust

C++

Java

创建

String::from("hi")

std::string s("hi")

new String("hi")

不可变引用

&str

std::string_view

String(本身不可变)

拼接

format! / +

s1 + s2 / +=

s1 + s2 / StringBuilder

长度(字节)

s.len()

s.length()

s.getBytes().length

长度(字符)

s.chars().count()

s.length()

索引访问

不支持

s[0]

s.charAt(0)

3.6.3 format! 与宏的第一印象#

Rust 中以 ! 结尾的调用通常是宏(macro),例如 format!println!vec!。宏不是普通函数:它在编译期展开成 Rust 代码,再参与类型检查和编译。

fn main() {
    let name = "Walter";
    let message = format!("Hello, {}!", name);

    println!("{}", message); // 输出: Hello, Walter!
}

format!("Hello, {}!", name) 做了三件事:

  1. 在编译期解析格式字符串里的 {} 占位符。

  2. 检查后面的参数数量和格式化能力是否匹配。

  3. 在运行时生成一个新的 String

这就是它比普通函数更适合做格式化的原因:普通函数无法接收“可变数量、可变类型”的参数列表,也无法在编译期理解格式字符串。

let user = "alice";
let count = 3;

let s1 = format!("user={user}, count={count}");
let s2 = format!("user={}, count={}", user, count);
let s3 = format!("debug={:?}", vec![1, 2, 3]);

常见格式化宏的区别:

返回值

用途

format!

String

生成字符串,不输出

println!

()

输出到标准输出并换行

eprintln!

()

输出到标准错误

write! / writeln!

fmt::Result

写入实现了 Write 的目标

宏的另一个典型例子是 vec!

let numbers = vec![1, 2, 3];

它展开后大致等价于创建一个 Vec 并依次 push 元素。你不需要现在就学会写宏,但要先形成两个直觉:

  • 看到 !,先想到“这是编译期展开的语法工具”。

  • 宏可以提供函数做不到的语法能力,但也要适度使用,业务抽象优先用函数和 trait。


3.7 集合类型速览#

3.7.1 Vec(动态数组)#

fn main() {
    // 创建
    let mut v: Vec<i32> = Vec::new();
    let v2 = vec![1, 2, 3];  // vec! 宏

    // 添加元素
    v.push(1);
    v.push(2);
    v.push(3);

    // 访问
    let third = &v[2];        // 直接索引(越界会 panic)
    let third = v.get(2);     // 返回 Option<&i32>(安全)

    match v.get(2) {
        Some(val) => println!("第三个元素: {}", val),
        None => println!("没有第三个元素"),
    }

    // 遍历
    for i in &v {
        println!("{}", i);
    }

    // 可变遍历
    for i in &mut v {
        *i += 10;
    }
    println!("{:?}", v); // [11, 12, 13]
}

3.7.2 HashMap#

use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();

    // 插入
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Red"), 50);

    // 访问
    let blue_score = scores.get("Blue");
    match blue_score {
        Some(score) => println!("Blue: {}", score),
        None => println!("Blue team not found"),
    }

    // 遍历
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }

    // 只在键不存在时插入
    scores.entry(String::from("Yellow")).or_insert(30);
    scores.entry(String::from("Blue")).or_insert(25); // 不会覆盖

    println!("{:?}", scores);
}

三语对照 — 集合:

集合

Rust

C++

Java

动态数组

Vec<T>

std::vector<T>

ArrayList<T>

哈希表

HashMap<K,V>

std::unordered_map<K,V>

HashMap<K,V>

有序映射

BTreeMap<K,V>

std::map<K,V>

TreeMap<K,V>

哈希集合

HashSet<T>

std::unordered_set<T>

HashSet<T>

双端队列

VecDeque<T>

std::deque<T>

ArrayDeque<T>

链表

LinkedList<T>

std::list<T>

LinkedList<T>


3.8 类型转换#

3.8.1 as 关键字#

fn main() {
    // 数值类型转换
    let x: i32 = 42;
    let y: i64 = x as i64;      // 安全:扩大范围
    let z: i8 = x as i8;        // ⚠️ 可能截断!42 → 42(OK)
    let w: u8 = -1_i8 as u8;    // ⚠️ 符号变化!-1 → 255

    // 浮点 ↔ 整数
    let f: f64 = 3.99;
    let i: i32 = f as i32;      // 截断小数部分:3
    let back: f64 = i as f64;   // 3.0

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

3.8.2 From / Into trait#

fn main() {
    // From:显式转换
    let s = String::from("hello");
    let n: i64 = i64::from(42_i32);

    // Into:隐式方向(通常由编译器推导)
    let n: i64 = 42_i32.into();

    // 自定义 From
    struct Celsius(f64);
    struct Fahrenheit(f64);

    impl From<Celsius> for Fahrenheit {
        fn from(c: Celsius) -> Self {
            Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
        }
    }

    let boiling = Celsius(100.0);
    let f: Fahrenheit = boiling.into();  // 自动获得 Into
    println!("100°C = {}°F", f.0);       // 100°C = 212°F
}

3.9 注释与文档#

// 行注释

/* 块注释 */

/// 文档注释(用于函数/结构体/模块)
/// 支持 Markdown 语法
///
/// # Examples
///
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
fn add(a: i32, b: i32) -> i32 {
    a + b
}

//! 模块级文档注释(放在文件开头)
//! 用于描述整个模块或 crate 的用途

亮点: Rust 的文档注释中的代码示例会被 cargo test 自动当作测试运行!这确保了文档中的示例永远不会过时。


3.10 小结#

本章我们快速浏览了 Rust 的基础语法,重点包括:

  • 变量默认不可变,需要 mut 才能修改——这是 Rust 安全哲学的体现

  • Rust 是表达式导向的语言ifmatchloop 都可以返回值

  • 模式匹配是 Rust 的核心特性,matchswitch 强大得多,且编译器保证穷尽性

  • 字符串有两种String(拥有所有权)和 &str(借用),理解它们的区别至关重要

  • 没有隐式类型转换——Rust 要求你对每次转换都心中有数

  • 文档注释中的代码会被自动测试——文档即测试

如果你来自 C++ 背景,你会觉得 Rust 在很多地方更严格但也更安全;如果你来自 Java 背景,你会发现 Rust 给了你更多的底层控制权,同时通过编译器保证了安全性。

下一章,我们将进入 Rust 最核心、最独特的概念——所有权系统。这是 Rust 区别于所有其他语言的关键特性,也是理解 Rust 的分水岭。