Rust 函数

Rust 函数的参数、返回类型、表达式返回、早期 return,以及与语句/表达式模型的关系。

#type / concept #status / growing #tech / dev #resource / rust

[!info] 关联笔记

Rust 函数

这个概念为什么出现

把逻辑从 main 拆出去之后,要稳定地约定:

  • 参数如何传入(值、引用——引用细节在所有权章)?
  • 返回值类型如何声明?
  • 何时用最后表达式,何时用 return
  • 函数如何成为后续 trait/方法/测试的基本单元?

[!abstract] 一句话理解 fn 声明命名函数;参数与返回类型在签名中显式出现(返回 () 可省略);函数体是表达式块,最后表达式作为返回值,return 用于提前退出。

最小可运行示例

场景:促销引擎计算折后价并校验数量

促销服务提供两个纯函数:校验购买数量、计算折后应付。
入口 main 只编排,便于单测与复用。

// 业务意图:
// 1. 数量必须 > 0;
// 2. 满 3 件 9 折(示意)。
// 教学点:
// - 显式参数/返回类型;
// - 表达式返回 vs 提前 return;
// - 单元测试函数。

fn ensure_positive_qty(qty: i32) -> Result<i32, String> {
    if qty <= 0 {
        // 提前返回错误
        return Err("qty must be positive".into());
    }
    Ok(qty) // 表达式返回 Ok
}

fn discounted_total_cents(unit_price_cents: i32, qty: i32) -> i32 {
    let raw = unit_price_cents * qty;
    // 最后表达式:满 3 件 9 折
    if qty >= 3 {
        raw * 90 / 100
    } else {
        raw
    }
}

fn main() {
    match ensure_positive_qty(3) {
        Ok(q) => {
            let total = discounted_total_cents(1_000, q);
            println!("total_cents={total}");
        }
        Err(e) => println!("invalid: {e}"),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn discounts_when_qty_at_least_3() {
        assert_eq!(discounted_total_cents(1_000, 3), 2_700);
    }

    #[test]
    fn rejects_non_positive_qty() {
        assert!(ensure_positive_qty(0).is_err());
    }
}

建议运行:

cargo test
cargo run

期望输出(cargo run):

total_cents=2700

期望测试:通过。

结合场景再看三个关注点

  1. 签名即契约:调用方不靠猜返回。
  2. 提前 return 处理守卫条件,主路径保持浅。
  3. 测试紧贴纯函数:折扣规则变更时测试先红。

核心概念与准确模型

签名

fn name(param: Type, ...) -> ReturnType {
    // body
}
  • 无返回值时等价 -> (),可省略
  • 参数在 Rust 中必须写类型(与局部 let 可推断不同)

返回

方式用法
尾表达式正常路径,无分号
return expr;提前退出
无返回返回 unit ()

语句与分号

fn bad() -> i32 {
    5; // 变成 (),与 i32 冲突
}

关联函数与方法(预告)

  • fn new() -> Self 常写在 impl 里(关联函数)
  • self/&self/&mut self 的是方法
    详见 struct 与方法

设计动机

  • 显式签名利于跨模块阅读与 API 稳定
  • 表达式返回减少样板,同时允许守卫式 return
  • 纯函数 + 测试是 Rust 工程默认肌肉记忆

边界情况与反直觉行为

  • 发散函数可返回 !(never),如永远 panic 的路径,属进阶。
  • 泛型函数 fn foo<T>(...)泛型
  • 异步函数 async fn 返回 impl Future,见 async/await

常见误区

[!warning] 常见误区:尾部多打分号导致类型错误 错误理解:分号只是风格。
正确模型:分号丢弃表达式值。
为何易错:从强制 return 语言迁来。

工程实践

  1. 小而纯的计算函数优先;I/O 与编排放边缘。
  2. 错误用 Result,不要用“魔法状态码”默默失败。
  3. 公共函数写清文档注释;示例可进文档测试。

本节总结

  • 函数签名显式;体为表达式块。
  • 尾表达式与 return 分工。
  • 函数是所有权、方法、测试的基础单元。

自测题

  1. 为什么函数参数通常不能省略类型?
  2. fn f() -> i32 { return 1; }fn f() -> i32 { 1 } 等价吗?
参考答案
  1. 签名是 API 契约,跨 crate 边界需要稳定明确的类型。
  2. 对这个简单例子语义等价;风格上短函数更常写尾表达式。

延伸阅读与资料来源

资料类型支撑内容
The Book — Functions官方书函数与表达式
Rust By Example — Functions官方示例
创建于 2026/7/15 更新于 2026/7/15