讲解
函数用 fn 定义,命名惯例是蛇形小写(snake_case):fn calculate_area()。参数必须标注类型——这是刻意设计:函数签名是调用双方的契约,参数类型让契约自文档化,也让函数体内部和调用点的报错都更精确。返回值类型用 -> 标注在参数列表之后。
理解 Rust 函数体必须先分清语句(statement)和表达式(expression):语句做事情、没有返回值(let 声明、函数调用加分号都是语句);表达式求值、产生一个值(字面量、算术运算、函数调用、甚至 {} 代码块和 if 都是表达式)。函数体的最后一个表达式(不加分号)就是返回值——这是 Rust 代码的主流风格,return 关键字只用于提前返回。
这套「一切皆表达式」的设计贯穿全语言:let y = if x > 0 { 1 } else { -1 } 合法,let n = { let a = 3; a + 1 } 也合法。代码块里最后一行不加分号,块的值就是那一行的值;一旦加了分号它就变成语句,块的值变成单元类型 ()——这是新手最高频的编译错误之一。
示例
参数、返回值与「最后一个表达式即返回值」:
fn add(a: i32, b: i32) -> i32 {
a + b // 没有分号:这是返回值
}
fn is_adult(age: u32) -> bool {
age >= 18
}
fn main() {
let sum = add(2, 3);
assert_eq!(sum, 5);
assert!(is_adult(20));
assert!(!is_adult(15));
println!("2 + 3 = {sum}");
}
代码块也是表达式;return 用于提前返回:
fn sign(n: i32) -> &'static str {
if n == 0 {
return "零"; // 提前返回必须用 return
}
if n > 0 { "正数" } else { "负数" }
}
fn main() {
let value = {
let base = 10;
base * 2 // 块的值是 20
};
assert_eq!(value, 20);
assert_eq!(sign(7), "正数");
assert_eq!(sign(-7), "负数");
assert_eq!(sign(0), "零");
println!("sign 全部判断正确");
}
常见坑
- 最后一行加了分号:fn f() -> i32 { 1 + 1; } 会报 mismatched types(期望 i32,实际得到 ())。看到 expected ... found unit type () 先检查分号。
- 参数忘标类型:Rust 不在函数边界做类型推断,fn f(x) 直接编译错误;标注是契约的一部分。
- 滥用 return:只有提前返回才需要 return,函数末尾的 return value; 是多余的,写成 value 更符合惯例。
- 命名用驼峰:函数名大写开头(fn AddTwo)会收到编译器警告;遵循 snake_case 是全社区统一约定。
小结
fn 定义、参数必标类型、-> 标返回值;函数体最后一个无分号表达式是返回值,return 只用于提前返回;代码块也是表达式。下一章把表达式特性用足:控制流。