Skip to content

函数与流程控制

函数与表达式导向语法、三大流程控制与 match 模式匹配入门。

函数:签名、尾表达式与发散

rust
fn main() {
    println!("{}", add(1, 2));
    println!("{}", max_of(3, 9));
    println!("{}", apply(double, 21));
    println!("{}", sign(-3));
}

fn add(a: i32, b: i32) -> i32 {
    a + b // 最后一行没有分号:它就是返回值
}

fn max_of(a: i32, b: i32) -> i32 {
    if a > b { a } else { b } // if 是表达式,直接当返回值
}

fn double(v: i32) -> i32 {
    v * 2
}

/// 函数作为值:`fn(i32) -> i32` 是函数指针类型,可以当参数传
fn apply(f: fn(i32) -> i32, v: i32) -> i32 {
    f(v)
}

fn sign(v: i32) -> i32 {
    if v < 0 {
        return -1; // 提前返回:必须显式写 return
    }
    if v == 0 {
        return 0;
    }
    1 // 兜底值
}

参数必须标类型

rust
// fn add(a, b) { a + b } // error: expected one of `:`, `@`, or `|`, found `,`

💡 对照:Python 用类型注解但运行期才用得上;JS 完全无类型;Java/Go 也要求写类型。Rust 的要求不是「啰嗦」,而是与类型推断的边界有关:函数是模块的公开接口,签名必须自洽可读,编译器不允许它依赖调用点来推断,否则库的接口会随调用方式而变。

分号陷阱:最后一个表达式

rust
fn sum_to(n: i32) -> i32 {
    let mut acc = 0;
    for i in 1..=n {
        acc += i;
    }
    acc // ← 有分号就变成语句,函数返回 ()
}

去掉 acc 的分号是正确写法。加上的话报错:

error[E0308]: mismatched types
 --> src\main.rs:1:22
  |
1 | fn sum_to(n: i32) -> i32 {
  |    ------            ^^^ expected `i32`, found `()`
  |    |
  |    implicitly returns `()` as its body has no tail or `return` expression
...
6 |     acc;
  |        - help: remove this semicolon to return this value

🧠 原理{ ... } 的值是块尾部表达式的值。acc; 是语句(statement),语句的值是 (),所以块的值变成 ()。这就是「Rust 里分号有语义」的原因,也是 expected (), found i32 这类报错最常见的来源。

发散函数 !(never type)

-> ! 表示「这个函数永不返回」。panic!todo!unreachable!std::process::exit 都是发散表达式。

rust
#[derive(Debug)]
enum Coin { Penny, Nickel }

fn value(c: Coin) -> i32 {
    match c {
        Coin::Penny => 1,
        Coin::Nickel => panic!("还没实现"), // panic! 是 !,可以当 i32 用
    }
}

fn parse_level(s: &str) -> u8 {
    let Ok(v) = s.parse::<u8>() else {
        todo!("待实现的输入:{s}"); // todo! 也是 !
    };
    v
}

fn main() {
    println!("{}", value(Coin::Penny));
    if std::env::args().count() > 99 {
        println!("{}", parse_level("3"));
    }
}

🧠 原理! 是「底类型(bottom type)」,它能强制转换(coerce)到任何类型——因为既然永不返回值,类型检查器就不必关心它「本该」是什么类型。这让 panic! 可以直接放在 match 的分支、if 的某一臂、let 的初始化位置,而不会引起类型不匹配。

宏/函数含义! 的关系
panic!("msg")立即终止当前线程并打印信息返回 !
todo!() / unimplemented!()占位:编译通过、运行到这里 panic返回 !
unreachable!()断言「逻辑上到不了这里」返回 !
assert!(cond) / assert_eq!(a, b)条件不成立则 panic返回 (),但分支内是 !
std::process::exit(1)立刻结束进程返回 !

⚠️ 陷阱todo!()运行期 panic,不是编译错误。把 todo!() 留在发布代码里,就等于在生产环境埋了个定时炸弹。想让它变成硬性检查,clippy::todo lint 可以帮忙。


语句 vs 表达式:Rust 最关键的一条语法差异

Rust 里几乎一切都是表达式:

语法结构是表达式吗它的值
{ ... }尾部表达式的值(没有尾部表达式则为 ()
if cond { a } else { b }被选中分支的值(两分支类型必须一致)
match x { ... }被选中分支的值
loop { ... }break value 的值
while / for否(值为 ()不能出现在需要值的位置
let 绑定否(是语句)无值,返回 ()
赋值 x = 1()
函数调用 f()返回类型
rust
fn main() {
    // 块作为表达式
    let area = {
        let w = 3;
        let h = 4;
        w * h // 块的「尾部表达式」就是块的值
    };

    // if 作为表达式(替代三目运算符)
    let n = 7;
    let parity = if n % 2 == 0 { "even" } else { "odd" };

    // match 作为表达式
    let word = match n {
        0 => "zero",
        1..=5 => "small",
        _ => "big",
    };

    println!("{area} {parity} {word}");

    // 赋值表达式的值是 ():所以不能写 a = b = 1
    let mut a = 0;
    a = 1;
    // let b = (a = 2); // error[E0308]: mismatched types:期望 i32,实际是 ()
}

输出:12 odd big

🧠 原理:为什么 Rust 坚持「赋值返回 ()」?因为如果 a = b 有值,就必然要定义「赋值的值是什么」(是被赋的值?还是旧值?),而 C 家族因此产生了 if (x = 0) 这类真实 bug。Rust 用 () 斩断了这种可能性,同时这也解释了为什么 while (line = read()) 在 Rust 里写不出来——必须用 while let Some(line) = read()loop { let line = ...; }

⚠️ 陷阱if 表达式若用作值,必须有 else 分支,否则类型是 ()if a { 1 }(无 else)不能赋给 i32


流程控制

if / else if / else

rust
fn main() {
    let score = 85;
    let grade = if score >= 90 {
        'A'
    } else if score >= 80 {
        'B'
    } else if score >= 60 {
        'C'
    } else {
        'F'
    };
    println!("{grade}"); // B
}

⚠️ 陷阱:条件必须是 bool没有 truthy/falsy。JS 里 if ([])if ("0") 均为真;Python 里 if [] 为假。Rust 里 if count { ... } 编译失败,if count = 1 也编译失败:

error[E0308]: mismatched types
 --> src\main.rs:3:8
  |
3 |     if count = 1 {
  |        ^^^^^^^^^ expected `bool`, found `()`
  |
help: you might have meant to compare for equality
  |
3 |     if count == 1 {
  |               +

loopwhilewhile let

rust
fn main() {
    // loop:无限循环,可以用 break 带出值
    let mut n = 0;
    let total = loop {
        n += 1;
        if n == 4 {
            break n * 10; // 循环的「返回值」
        }
    };
    println!("{total}"); // 40

    // while:条件为 bool
    let mut k = 3;
    while k > 0 {
        k -= 1;
    }
    println!("{k}"); // 0

    // while let:「每次迭代解构一个可能失败的模式」
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() {
        print!("{top} "); // 3 2 1
    }
    println!();

    // loop + break 用于「没有自然条件」的循环:比 while true + 哨兵变量更清晰
    let mut guess = 0;
    let found = loop {
        guess += 1;
        if guess * guess >= 20 {
            break guess;
        }
    };
    println!("{found}"); // 5
}

💡 对照:Java 用 do { } while (cond),Rust 没有 do-while;惯用替代是 loop { ...; if !cond { break; } }。Python 的 while ... else、JS 的 do...while 都没有对应物,因为 break value 和标签已经覆盖了绝大多数「循环带出结果」的需求。

for ... in 与三种迭代形式

for 的底层是 IntoIterator trait(〈并发编程〉一章展开)。现在只需要会区分这三种写法:

写法迭代产生元素类型循环体能否修改元素循环后原集合能否使用类比
for v in &xs共享借用&T否(只能读)Python 的 for v in xs(只读语义)
for v in &mut xs可变借用&mut T能(*v = ...无直接对应
for v in xs(或 xs.into_iter()取得所有权T能(拿到的是值)不能(被移动了)Python 的 for v in xs(但 Rust 会移动)
for i in 0..n半开区间i32/推断整型不适用range(n)
for i in 0..=n闭区间,含 n整数不适用range(n + 1)
rust
fn main() {
    let mut nums = vec![1, 2, 3];

    for v in &nums {
        print!("{v}");   // 1 2 3:只读借用
    }
    println!();

    for v in &mut nums {
        *v *= 2;         // 通过可变借用改元素
    }
    println!("{nums:?}"); // [2, 4, 6]

    for v in nums {      // 取得所有权:nums 之后不能再用
        print!("{v}");   // 2 4 6
    }
    println!();

    for i in 0..3 {
        print!("{i}");   // 0 1 2
    }
    println!();

    for i in 0..=3 {
        print!("{i}");   // 0 1 2 3
    }
    println!();

    // 显式方法形式:.iter() / .iter_mut() / .into_iter()
    let vals = vec![10, 20];
    for v in vals.iter() {
        print!("{v} ");  // 10 20
    }
    println!();
    for v in vals.into_iter() {
        print!("{v} ");  // 10 20
    }
    println!();
}

🧠 原理for v in &xsfor v in (&xs).into_iter() 的语法糖;&Vec<T> 实现了 IntoIterator<Item = &T>&mut Vec<T> 实现 IntoIterator<Item = &mut T>Vec<T> 自己实现 IntoIterator<Item = T>同一个 for 关键字,元素类型由你给的集合形式决定——这就是为什么 Rust 的 for 不需要 .items()/.values() 之类的辅助方法,也不会像 JS 的 for...in 那样给出字符串键。

⚠️ 陷阱for v in xs 之后再用 xs 会得到 error[E0382]: use of moved value: xs。新手最常见的一类报错就来自这里——解决办法是按需改成 &xs.iter()

break / continue 与标签

rust
fn main() {
    let grid = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];

    'rows: for (r, row) in grid.iter().enumerate() {
        for (c, &v) in row.iter().enumerate() {
            if v == 5 {
                println!("found 5 at ({r}, {c})");
                break 'rows; // 直接跳出外层循环
            }
            if v % 2 == 0 {
                continue; // 只跳过内层本次迭代
            }
            print!("{v} ");
        }
    }
    println!();
}

输出:1 3 found 5 at (1, 1)——内层 continue 只跳过当前格,break 'rows 直接结束两层循环,所以 7 9 没有被打出来。

  • 标签(label)写作 '名字:,作用于 loop/while/for,可被 break 'name(可带值)和 continue 'name 使用。
  • 循环也是表达式:loop { break 1; } 的类型是 i32while/for 的值是 (),所以不能 let x = for ... {}

模式匹配:matchif letlet else

match 是 Rust 的瑞士军刀:它同时做分支解构,并且必须穷尽

穷尽性、_ 与绑定

rust
fn main() {
    let n = 3;
    let word = match n {
        0 => "zero",
        1..=5 => "small",   // 范围模式:闭区间
        6 | 7 | 8 => "mid", // 或模式
        _ => "big",         // 通配:必须兜底
    };
    println!("{word}"); // small

    // 绑定:把整个值绑到名字上
    let x = 42;
    match x {
        hit @ 40..=49 => println!("hit {hit}"),
        other => println!("other {other}"),
    }

    // 守卫(guard):在模式之后追加 if 条件
    let pair = (3, 7);
    match pair {
        (a, b) if a == b => println!("diagonal {a}"),
        (a @ 1..=3, b) => println!("low {a} {b}"),
        _ => println!("other"),
    }
}

输出:smallhit 42low 3 7

🧠 原理match 的穷尽性检查是编译期的。编译器会用穷尽性算法(exhaustiveness checking)验证所有可能值都被覆盖,覆盖不到就报 error[E0004]: non-exhaustive patterns。这就是为什么「忘写 _」只会在编译期炸,而不会像 C 的 switch 那样悄悄跳过——C 家族里忘记 default 的代价是运行期拿到未初始化的垃圾值。

⚠️ 陷阱:绑定会把所有值都匹配掉,所以 other => ... 之后的臂是死代码。同理,n => ... 写在前面的臂会让后面的臂永远不可达,编译器一般会给出 unreachable pattern 警告。

解构元组、数组、结构体、枚举

rust
#[derive(Debug)]
struct Point { x: i32, y: i32 }

fn main() {
    // 元组
    let (a, b) = (1, 'x');
    println!("{a} {b}");

    // 数组/切片模式
    match &[1, 2, 3][..] {
        [] => println!("empty"),
        [only] => println!("one: {only}"),
        [first, .., last] => println!("{first} .. {last}"), // 1 .. 3
    }

    // 结构体:按字段名解构(不需要顺序一致)
    let p = Point { x: 1, y: 2 };
    let Point { x, y } = p;
    println!("{x} {y}");
    let Point { x: px, .. } = p; // 只取 x,其余用 .. 忽略
    println!("{px}");

    // 枚举 + Option/Result(完整讲解见〈集合与迭代器〉)
    let maybe: Option<i32> = Some(5);
    match maybe {
        Some(v) if v > 3 => println!("big {v}"),
        Some(v) => println!("small {v}"),
        None => println!("none"),
    }

    let parsed: Result<i32, _> = "7".parse();
    match parsed {
        Ok(v) => println!("ok {v}"),
        Err(e) => println!("err {e}"),
    }

    // matches! 宏:只想知道「是否匹配」,不需要解构
    println!("{}", matches!(maybe, Some(v) if v > 3)); // true
    println!("{}", matches!('q', 'a'..='m' | 'A'..='M')); // false
}

输出:1 x1 .. 31 21big 5ok 7truefalse

💡 对照:Java 的 switch 只能匹配常量(现代 Java 支持模式匹配,但必须写 default 才不会漏);Python 的 match 是 3.10+ 且不检查穷尽性;Go 的 switch 默认不贯穿。Rust 的 match 三者兼有:能解构、能守卫、且强制穷尽。

if letif let ... elselet else(1.65+)

rust
fn main() {
    let maybe: Option<i32> = Some(1);

    // if let:只关心一个模式时,比 match 少一层缩进
    if let Some(v) = maybe {
        println!("got {v}");
    } else {
        println!("nothing");
    }

    // 多条件串联(if let 链):Rust 2024 起可直接用 && 连接(1.88.0 稳定)
    let config: Option<(u8, &str)> = Some((3, "debug"));
    if let Some((level, _)) = config
        && level >= 3
    {
        println!("verbose");
    }

    // let else:`let PATTERN = expr else { ... }`,失败分支必须发散(return/break/panic!)
    let text = "2024";
    let Ok(year) = text.parse::<u32>() else {
        println!("解析失败");
        return;
    };
    println!("year = {year}");

    // 用 let else 做「早退卫语句」,避免层层嵌套
    let first = [3, 1, 2].first().copied();
    let Some(head) = first else {
        println!("空数组");
        return;
    };
    println!("head = {head}");
}

输出:got 1verboseyear = 2024head = 3

⚠️ 陷阱let else 右侧的模式必须是可反驳(refutable)的,并且 else 块必须发散(类型为 !)。写 let Some(x) = maybe else { 0 }; 会报类型错误,因为 0 不是 !。另外 let Ok(v) = ... 里的绑定在 else 块中不可见


注释与文档注释

rust
//! 模块级文档注释(inner doc):写在文件/模块顶部,描述这个模块本身。
//! 只能出现在模块开头——所以它必须放在所有 item(包括 `use`)之前。

// 单行注释:解释「为什么」,不是「做了什么」
/* 块注释:可以 /* 嵌套 */,因此可以临时注释掉整段带注释的代码 */

/// 项目级文档注释(outer doc):给紧跟其后的项写文档,支持 Markdown。
///
/// # Examples
///
/// ```
/// let r = my_crate::double(21);
/// assert_eq!(r, 42);
/// ```
fn double(v: i32) -> i32 {
    v * 2
}

#[doc = "属性形式的文档注释,等价于 `///`,常用于宏生成的代码"]
pub const LIMIT: u32 = 100;

fn main() {
    println!("{}", double(21));

    // SAFETY: 无 unsafe 操作,这里只是演示注释约定(见下)
    let _ = LIMIT;
}
  • ///:为紧随其后的项(函数、结构体、模块声明)写文档,cargo doc 会渲染成 HTML。
  • //!:为包含它的模块/crate 写文档,必须位于文件顶部(//! 是 inner attribute,/// 是 outer)。
  • #[doc = "..."]:属性形式,等价于 ///;宏生成文档时只能用它。
  • // SAFETY: 约定:每个 unsafe 块之前都应写清「为什么这里满足安全前提」。这是社区标准(clippy::undocumented_unsafe_blocks 会检查),因为 unsafe 的正确性依赖的推理,代码本身无法表达。

🚀 进阶:文档里的代码块会被 cargo test 当作 doctest 编译并运行,所以写文档示例时它会自动帮你验证代码不会过时。想深入打磨 API 文档,可查阅 Rustdoc 官方手册(入口见附录 D)。


与其他语言的对照

主题PythonJavaJS/TSC/C++GoRust 的做法与原因
变量可变性全部可变final 才不可变const/let 只是重绑定限制默认可变默认可变默认不可变,要写 let mut;把「会不会被改」写进类型检查
常量无真正的常量static finalconst(对象内容仍可变)#define/constconst/varconst 编译期内联,static 有地址;static mutunsafe
条件表达式a if c else bc ? a : bc ? a : bc ? a : b无三目直接用 if c { a } else { b },因为 if 本身就是表达式
多分支if/elif、3.10+ matchswitch(易忘 defaultswitch=== 陷阱)/ifswitch(默认贯穿)switch(不贯穿)match 强制穷尽、可解构、可守卫;漏分支是编译错误
for 形式for x in itfor (T x : c)for...of/for...infor (i=0;…)for i, v := rangefor v in xs/&xs/&mut xs/0..n,元素类型由迭代形式决定
自增运算符++i++ 有值i++ 有值i++ 有值i++ 是语句没有 ++/--;用 += 1。因为赋值返回 (),无法定义「后缀返回旧值」
整数溢出任意精度,不溢出静默环绕双精度浮点,超 2⁵³ 丢精度有符号溢出是 UB静默环绕debug panic、release 环绕(有定义),并提供 checked_*/wrapping_* 等显式 API
数值隐式转换自动提升自动提升弱类型自动转整型提升、隐式窄化需显式转换完全禁止隐式转换as 是显式且可能丢数据,From/TryFrom 表达意图
字符串可变性str 不可变String 不可变不可变(需新建)char* 可改string 不可变String(可增长)与 &str(借用视图)分离,UTF-8 保证由类型系统维护
nullNonenull(可赋给任何引用)null/undefinedNULL/nullptrnil没有 null;用 Option<T> 强制处理「可能没有」的情况
数组与列表list 动态、tuple 定长数组定长、ArrayList 动态Array 动态数组退化指针、std::vector数组定长、切片动态数组 [T; N] 长度进类型、&[T] 借用视图、Vec<T> 拥有堆数据
错误处理异常受检/非受检异常异常 + Promise reject错误码/异常多返回值 errorResult<T, E> 是普通值;? 传播(〈错误处理〉一章)
函数返回值return/尾表达式必须 return必须 return必须 return必须 return尾部表达式即返回值,return 只用于提前退出
未使用变量无警告无警告无警告警告编译错误警告(unused_variables),_ 前缀显式表示「故意不用」

常见坑与编译错误

坑 1:cannot find value / cannot find function

error[E0425]: cannot find value `y` in this scope
 --> src\main.rs:2:13
  |
2 |     let x = y + 1;
  |             ^ not found in this scope

原因:名字拼错、变量还没定义、或者忘了 use(例如 HashMap 需要 use std::collections::HashMap;)。 修法:检查拼写与定义顺序;需要导入时按编译器提示补 use;作用域不同时把定义提到使用之前。

坑 2:忘记分号 → expected (), found i32

error[E0308]: mismatched types
 --> src\main.rs:1:22
  |
1 | fn sum_to(n: i32) -> i32 {
  |    ------            ^^^ expected `i32`, found `()`
  |    |
  |    implicitly returns `()` as its body has no tail or `return` expression
...
6 |     acc;
  |        - help: remove this semicolon to return this value

原因:函数体最后一个表达式后面多了 ;,块的值就变成 ()修法:删掉那个分号,或用 return acc;。块内同理(let total = { ...; acc };)。

坑 3:if 两分支类型不一致

error[E0308]: `if` and `else` have incompatible types
 --> src\main.rs:3:34
  |
3 |     let n = if flag { 1 } else { "zero" };
  |                       -          ^^^^^^ expected integer, found `&str`

原因if 当表达式用时,两分支必须是同一类型if 没有公共父类型可退,enum 才是 Rust 表达「二者之一」的方式)。 修法:统一类型,或把结果包成 enum/Option/Result,或改成「两个分支各自 println!」而不是取值。

坑 4:match 非穷尽

error[E0004]: non-exhaustive patterns: `i32::MIN..=0_i32` and `3_i32..=i32::MAX` not covered
 --> src\main.rs:3:22
  |
3 |     let word = match n {
  |                      ^ patterns `i32::MIN..=0_i32` and `3_i32..=i32::MAX` not covered
  |
  = note: the matched value is of type `i32`

原因:漏了分支。注意编译器会精确告诉你是哪些区间没覆盖。 修法:补上剩余分支,或加 _ => ... 兜底。若用 _,建议在注释里写明「这里有意忽略 X」,否则以后枚举加变体会静默走兜底分支。

坑 5:把 String&str 混用

error[E0308]: mismatched types
 --> src\main.rs:3:11
  |
3 |     greet(name);
  |     ----- ^^^^ expected `&str`, found `String`
  |     |
  |     arguments to this function are incorrect
  |
help: consider borrowing here
  |
3 |     greet(&name);
  |           +

原因String 不自动变成 &str 作为函数实参(&String 会在需要 &str 时经 Deref 自动转换,但 String 本身不会)。 修法:传 &name(顺带避免移动);函数参数尽量写成 &str,这样数组、字面量、String 借用都能传。

坑 6:as 静默截断

rust
fn main() {
    let big = 300i32;
    let small = big as u8; // 不报错、不 panic,结果是 44
    println!("{small}");
}

原因as 的语义就是「按位窄化」。编译器默认不警告(clippy::cast_possible_truncation 可以开启检查)。 修法:用 u8::try_from(big) / big.try_into() 明确处理失败,或 checked_* 系列,并注释说明为何断言截断是安全的。

坑 7:unused variable / 未使用的 mut

warning: unused variable: `unused`
 --> src\main.rs:2:9
  |
2 |     let unused = 10;
  |         ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused`
warning: variable does not need to be mutable
 --> src\main.rs:2:9
  |
2 |     let mut x = 1;
  |         ----^
  |         |
  |         help: remove this `mut`

原因:变量从未被读 / 从未被写。 修法:能删就删;确实需要占位时用 _name(保留绑定,仍会析构)或 _(不绑定)。别用 #[allow(unused_variables)] 一把梭——它会掩盖真正的遗漏。

坑 8:移动后再使用(借用检查器直觉)

error[E0382]: borrow of moved value: `text`
  --> src\main.rs:12:21
   |
10 |     let text = String::from("hi");
   |         ---- move occurs because `text` has type `String`, which does not implement the `Copy` trait
11 |     let t2: String = text;
   |                      ---- value moved here
12 |     println!("{t2} {text}");
   |                     ^^^^ value borrowed here after move

原因String 没有实现 Copy,赋值/传参是移动而非复制;移动后原绑定失效。i32boolchar&T 这类实现了 Copy 的类型不受影响。 修法:需要两份数据就 .clone()(要付出分配成本);只想借用就传 &text;把所有权给了函数又想要回来,就让函数返回它(〈所有权〉一章会给出更好的方案)。

🧠 原理:移动检查发生在编译期,基于每个绑定的「有效/失效」状态做数据流分析。它的目标是:任何时刻对某个值最多只有一个所有者,从而释放内存时不会出现双重释放(double free)或用后释放(use after free)。


速查表

想做的事写法
不可变绑定 / 可变绑定let x = 1; / let mut x = 1;
改变量类型或改名遮蔽:let x = x as f64;
编译期常量 / 全局const N: usize = 4; / static NAME: &str = "x";
整数类型选择默认 i32;索引/长度用 usize;位运算用 u8/u32;大计数用 i64/u64
安全算术a.checked_add(b)Optiona.wrapping_add(b)a.saturating_add(b)a.overflowing_add(b)
类型转换拓宽 i64::from(x)/x.into();可能失败 u8::try_from(x)/x.try_into();位级 x as u8
解析文本"42".parse::<i32>()?(或 .expect("...")
元组解构 / 取字段let (a, b) = t; / t.0
数组 / 切片[T; N] / &arr[1..]&s[..2]
字符串字面量 → 拥有"x".to_string()String::from("x")format!("{a}{b}")
提前返回 / 发散return v; / panic!todo!unreachable!(类型 !
块/if/match 取值尾部不加分号:let v = { f(); 42 };
条件(必须 boolif x > 0 { } / if let Some(v) = o { }
循环取结果let v = loop { break 42; };
跳出外层循环'outer: for ... { break 'outer; }
迭代三种方式for v in &xs / for v in &mut xs / for v in xs
穷尽分支match v { ... },必要时 _ => ...
只匹配一个模式if let Some(v) = o { } else { };早退用 let ... else { return; }
只判断是否匹配matches!(v, Pattern)
忽略未使用值let _ = f();(不绑定)/ let _x = f();(绑定但静默)

内容以 rustc 1.98.1 · Rust 2024 edition 为基准