Rust 学习笔记 03:函数

“Talk is cheap. Show me the code.” – Linus Torvalds

在这一课,我们来聊聊编程语言中最基础的构建块——函数。

如果你熟悉 Go,你可能会觉得:“函数嘛,不就是 func 换成了 fn,大括号里写逻辑吗?”

确实,表面上看区别不大。但 Rust 引入了一个对 Go 开发者来说比较新颖的概念:语句 (Statements) 与 表达式 (Expressions) 的严格区分。这直接改变了代码的书写习惯(和逼格)。

1. 定义函数

Rust 使用 snake_case (蛇形命名法) 来命名函数,所有字母小写,单词间用下划线分开。

fn main() {
    println!("Hello, world!");
    another_function();
}

fn another_function() {
    println!("Another function.");
}

Go 也是这么写的(除了大括号位置 Go 强制不换行,Rust 推荐不换行但没强制)。

2. 参数与类型

和 Go 一样,Rust 是强类型语言,函数参数必须声明类型。这点没得商量。

fn print_labeled_measurement(value: i32, unit_label: &str) {
    println!("The measurement is: {}{}", value, unit_label);
}

3. 语句 vs 表达式 (The Twist)

这是本节的重点!

  • 语句 (Statements):执行一些操作,但不返回值。
  • 表达式 (Expressions):执行计算并产生一个值

在 Go 中,我们很少区分这个。但在 Rust 中:

// 这是一个语句
let y = 6; 

// 试图把语句赋值给变量会报错,因为语句没有返回值(或者说返回了单元类型 ())
// let x = (let y = 6); // Error!

但是,Rust 的代码块 {} 是一个表达式

let y = {
    let x = 3;
    x + 1 // 注意!这行没有分号!
};

println!("The value of y is: {}", y); // y is 4

请千万注意 x + 1 后面没有分号

  • 没有分号 -> 这是一个表达式 -> 返回 x + 1 的值 (4)。
  • 加了分号 -> x + 1; -> 这是一个语句 -> 执行加法但丢弃结果 -> 返回 ()

这就像是:

  • 表达式:你去肯德基点个餐,服务员给你一个汉堡(返回值)。
  • 语句:你去肯德基上个厕所,做完了就走了,手里没拿东西(返回 ())。

4. 返回值

Rust 函数的返回值不需要命名(Go 可以命名返回值),只需在箭头 -> 后声明类型。

隐式返回 (Implicit Return)

在 Rust 中,函数体的最后一个表达式自动作为返回值。你不需要写 return 关键字(虽然你也可以写,用于提前返回)。

fn five() -> i32 {
    5 // 等同于 return 5;
}

fn plus_one(x: i32) -> i32 {
    x + 1
}

如果你手滑给 5 加上了分号:

fn five() -> i32 {
    5; // 变成语句了,返回 ()
}

编译器会立刻报错:mismatched types: expected i32, found ()。 意思是:“大哥,你承诺返回个整数,结果你啥也没给我(给了个寂寞)?”

这种写法让 Rust 代码可以通过组合表达式变得非常紧凑和函数式。

5. 小结

第三次笔记核心:

  • fn 定义函数,参数必须标类型。
  • 分号决定命运:没分号是表达式(有值),有分号是语句(无值)。
  • 函数最后一行通常不写 return,直接放一个表达式即可。

习惯了"隐式返回"后,你会觉得每次都要敲 return 真的挺累的。

下一篇,我们将看看 Rust 的控制流程。既然 if 也是表达式,那是不是意味着我们可以 let x = if ... ?是的,可以!


练习题

  1. 写一个函数 square,接收一个 i32,返回它的平方。记得使用隐式返回。
  2. 尝试在 main 函数中写一个代码块,计算两个变量的和并赋值给第三个变量。

思考题

为什么 Rust 区分语句和表达式?这种设计对编写"函数式风格"的代码有什么帮助?


本文代码示例


相关阅读