函数与控制流:让代码学会“思考”和“重复”

各位同学,今天我们来聊聊 Rust 里最核心的两个基本功——函数控制流。说白了,函数就是给代码块起个名字,方便反复调用;控制流就是让代码根据条件走不同的路,或者重复执行某段逻辑。这两样东西掌握好了,你就能写出有“脑子”的程序。

函数定义与参数

Rust 的函数用 fn 关键字声明。我个人习惯把函数名写成蛇形命名法(snake_case),比如 calculate_area。参数必须写明类型,这一点和 TypeScript 有点像,但 Rust 更严格——它不允许类型推断发生在函数签名里。

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

嗯,这里要注意:&str 是字符串切片,它不会拿走数据的所有权。我在项目中遇到过新手直接把 String 传进去,结果函数用完之后原变量就不能用了——这就是所有权转移的坑。

语句与表达式

Rust 里有个很重要的区分:语句(statement)和表达式(expression)。语句执行动作,不返回值;表达式计算值,可以返回。你想想看,这其实很像自然语言里的“祈使句”和“疑问句”。

let x = 5;      // 语句
let y = {        // 代码块是表达式
    let a = 3;
    a + 2        // 没有分号,这就是返回值
};

我曾经在写复杂逻辑时,忘记在代码块末尾加分号,结果编译器报错说类型不匹配。后来我养成了习惯:想返回就省略分号,不想返回就加分号。这个规则简单粗暴,但很管用。

核心要点:Rust 中几乎所有东西都是表达式,包括 ifmatch。这意味着你可以把条件判断的结果直接赋值给变量。

返回值

函数返回值用 -> 箭头指定类型。最后一个表达式的值就是返回值,也可以用 return 提前返回。我个人更倾向于用隐式返回,除非需要提前退出。

fn add(a: i32, b: i32) -> i32 {
    a + b   // 隐式返回
}

fn early_return(x: i32) -> i32 {
    if x < 0 {
        return 0;  // 提前返回
    }
    x * 2
}

我记得有一次写递归函数,忘记写返回类型,编译器直接报错。Rust 在这方面很严格——所有函数都必须显式声明返回类型,除了 ()(单元类型)可以省略。

if/else 分支

Rust 的 if 条件不需要括号,但必须用花括号。这一点和 Go 很像。而且 if 本身是表达式,可以返回值。

let score = 85;
let grade = if score >= 90 {
    "A"
} else if score >= 80 {
    "B"
} else {
    "C"
};

这里有个坑:所有分支返回的类型必须一致。我曾经在项目中写了一个 if 表达式,一个分支返回整数,另一个返回字符串,编译器直接拒绝编译。嗯,这其实是好事——它逼着你写出类型安全的代码。

避坑指南:我曾经在 if 表达式里忘记在最后加分号,导致整个表达式被当作语句处理,变量赋值失败。记住:if 作为表达式使用时,整个语句末尾必须加分号。

循环:loop、while、for

Rust 提供了三种循环方式,各有各的适用场景。

loop:无限循环

loop 会一直执行,直到你主动 break。它也可以返回值,这个特性很实用。

let mut counter = 0;
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2;  // 返回 20
    }
};

我个人习惯用 loop 做事件循环或重试逻辑。比如网络请求失败时,用 loop 配合 break 实现最多重试三次的逻辑。

while:条件循环

while 在条件为真时反复执行。它比 loop 更安全,因为至少有个退出条件。

let mut n = 3;
while n > 0 {
    println!("{}!", n);
    n -= 1;
}

你想想看,while 其实等价于 loopif 判断。但 while 更简洁,语义也更清晰。

for:遍历循环

for 是 Rust 里最常用的循环方式,用来遍历迭代器。它比 C 风格的 for (i=0; i<n; i++) 更安全,不会出现越界问题。

let arr = [10, 20, 30];
for element in arr.iter() {
    println!("值: {}", element);
}

// 范围语法
for i in 0..5 {
    println!("{}", i);  // 输出 0 到 4
}

我在项目中几乎只用 for 循环,除非需要无限循环或条件循环。它配合迭代器可以写出非常优雅的代码。

break 与 continue

break 跳出整个循环,continue 跳过当前迭代进入下一次。这两个关键字在嵌套循环中特别有用,可以配合标签使用。

'outer: for i in 0..5 {
    for j in 0..5 {
        if i * j > 10 {
            break 'outer;  // 跳出外层循环
        }
        println!("({}, {})", i, j);
    }
}

我曾经在解析嵌套数据结构时,用标签 break 直接跳出多层循环,代码瞬间变得清晰。没有标签的话,你得用 flag 变量,那代码就丑了。

小技巧break 后面可以跟表达式,用于从 loop 返回值。但 whilefor 不支持这个特性,只有 loop 可以。

知识体系图

下面这张图帮你理清函数与控制流的核心脉络:

函数与控制流知识体系 函数 • fn 关键字声明 • 参数必须标明类型 • 语句 vs 表达式 • 隐式返回 / return 控制流 • if/else 分支(表达式) • loop 无限循环 • while 条件循环 • for 遍历循环 循环控制 • break:跳出循环(可带标签) • continue:跳过当前迭代 • 标签 'label:用于嵌套循环

这张图把函数和控制流分成了两大块,中间用虚线连接——因为函数体里必然包含控制流,而控制流也经常调用函数。底部的循环控制是两者的交叉点,breakcontinue 在循环中扮演着“交通警察”的角色。

实战建议

学到这里,你应该能写出带逻辑判断和循环的 Rust 程序了。我个人建议你从一个小练习开始:写一个猜数字游戏,用 loop 实现游戏循环,用 if/else 判断猜大猜小,用 break 在猜对时退出。这个练习几乎涵盖了今天讲的所有知识点。

记住:函数让代码可复用,控制流让代码有逻辑。两者结合,你就能构建出真正有用的程序。下一章我们会深入所有权和借用,那才是 Rust 真正的“硬核”部分——但有了函数和控制流的基础,你会更容易理解。