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
期望测试:通过。
结合场景再看三个关注点
- 签名即契约:调用方不靠猜返回。
- 提前 return 处理守卫条件,主路径保持浅。
- 测试紧贴纯函数:折扣规则变更时测试先红。
核心概念与准确模型
签名
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 语言迁来。
工程实践
- 小而纯的计算函数优先;I/O 与编排放边缘。
- 错误用
Result,不要用“魔法状态码”默默失败。 - 公共函数写清文档注释;示例可进文档测试。
本节总结
- 函数签名显式;体为表达式块。
- 尾表达式与
return分工。 - 函数是所有权、方法、测试的基础单元。
自测题
- 为什么函数参数通常不能省略类型?
fn f() -> i32 { return 1; }与fn f() -> i32 { 1 }等价吗?
参考答案
- 签名是 API 契约,跨 crate 边界需要稳定明确的类型。
- 对这个简单例子语义等价;风格上短函数更常写尾表达式。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| The Book — Functions | 官方书 | 函数与表达式 |
| Rust By Example — Functions | 官方 | 示例 |