文章目录
-
- 一、回顾与导入
- 二、函数基础
- [三、语句 vs 表达式 ------ 核心概念](#三、语句 vs 表达式 —— 核心概念)
- 四、函数返回值
-
- [方式一:使用 `return` 关键字(早期返回)](#方式一:使用
return关键字(早期返回)) - 方式二:隐式返回(表达式)
- 返回多个值:使用元组
- [返回类型 `-> ()`(空元组)](#返回类型
-> ()(空元组))
- [方式一:使用 `return` 关键字(早期返回)](#方式一:使用
- 五、常见错误与陷阱
-
- [陷阱 1:忘记在最后一个表达式加分号会导致不同行为](#陷阱 1:忘记在最后一个表达式加分号会导致不同行为)
- [陷阱 2:分号的位置决定了是语句还是表达式](#陷阱 2:分号的位置决定了是语句还是表达式)
- 六、注释:三种注释风格
-
- [1. 普通注释](#1. 普通注释)
- [2. 文档注释 `///`](#2. 文档注释
///) - [3. 内部文档注释 `//!`](#3. 内部文档注释
//!) - 文档注释测试
- 七、完整代码示例
- 八、总结与速查
- 九、思考题
- 参考链接
掌握 Rust 函数的核心语法 ------ 表达式、返回值与三种注释
一、回顾与导入
前几篇我们学习了:
- 变量、可变性、常量与 shadowing
- 基础数据类型(标量与复合类型)
今天我们来学习 Rust 中的函数 。函数在 Rust 中无处不在 ------ main 函数就是程序的入口。Rust 的函数设计有一些独特的特性(比如表达式 vs 语句),掌握这些会帮你写出更地道的 Rust 代码。
二、函数基础
函数定义与命名
使用 fn 关键字定义函数,命名采用 snake_case 风格:
rust
fn main() {
println!("Hello from main!");
say_hello();
print_number(42);
}
fn say_hello() {
println!("Hello, world!");
}
fn print_number(x: i32) {
println!("The number is: {}", x);
}
参数传递
Rust 函数的参数必须显式声明类型(编译器不会推断函数签名中的类型):
rust
fn main() {
print_labeled_measurement(10, 'm');
}
fn print_labeled_measurement(value: i32, unit_label: char) {
println!("The measurement is: {}{}", value, unit_label);
}
三、语句 vs 表达式 ------ 核心概念
这是 Rust 中一个非常重要的区别:
| 概念 | 特点 | 例子 | 返回值 |
|---|---|---|---|
| 语句(Statement) | 执行操作,不返回值 | let x = 5; |
无(()) |
| 表达式(Expression) | 计算结果值 | 5 + 3、x + 1、函数调用 |
有值 |
💡 核心规则:
- 语句以分号
;结尾- 表达式不以分号结尾(加了分号就变成语句了)
rust
fn main() {
// 语句
let x = 5; // 这是一个语句,不返回值
// 表达式
let y = { // 代码块也是表达式,返回最后一个表达式的值
let a = 3;
let b = 4;
a + b // 没有分号!这是一个表达式
};
println!("y = {}", y); // y = 7
// 表达式可以作为函数调用的参数
println!("{}", 5 + 3); // 5 + 3 是一个表达式,值为 8
// 注意:加上了分号就会变成语句
let z = {
let a = 3;
a + 1; // 这里有分号!变成了语句,值为 ()
};
println!("z = {:?}", z); // z = ()
}
四、函数返回值
函数可以通过两种方式返回值:
方式一:使用 return 关键字(早期返回)
rust
fn is_even(x: i32) -> bool {
if x % 2 == 0 {
return true; // 早期返回
}
false // 隐式返回(表达式)
}
方式二:隐式返回(表达式)
函数体中最后一个表达式的值 会作为返回值,不需要 return 关键字:
rust
fn add(a: i32, b: i32) -> i32 {
a + b // 没有分号!这是表达式,作为返回值
}
fn main() {
let result = add(5, 3);
println!("5 + 3 = {}", result);
}
返回多个值:使用元组
rust
fn swap(x: i32, y: i32) -> (i32, i32) {
(y, x) // 返回元组
}
fn main() {
let (a, b) = swap(10, 20);
println!("a = {}, b = {}", a, b); // a = 20, b = 10
}
返回类型 -> ()(空元组)
如果函数没有返回值,实际上是返回了空元组 ():
rust
fn do_nothing() {
// 隐式返回 ()
}
// 等价于
fn do_nothing_explicit() -> () {
return ();
}
五、常见错误与陷阱
陷阱 1:忘记在最后一个表达式加分号会导致不同行为
rust
// 错误示范 - 本意是返回 5
fn wrong() -> i32 {
5; // 加上了分号变成了语句,返回值变成了 ()
}
// error[E0308]: mismatched types, expected `i32`, found `()`
// 正确写法
fn correct() -> i32 {
5 // 没有分号
}
陷阱 2:分号的位置决定了是语句还是表达式
rust
fn main() {
// 这是两个语句
let x = 5;
let y = 6;
// 这是一个表达式(整个代码块的值为 7)
let z = {
let a = 3;
let b = 4;
a + b // 没有分号
};
println!("z = {}", z);
// 如果代码块最后加分号,返回 ()
let w = {
let a = 3;
let b = 4;
a + b; // 有分号
};
println!("w = {:?}", w); // ()
}
六、注释:三种注释风格
Rust 提供了三种注释:
| 类型 | 语法 | 用途 | 是否生成文档 |
|---|---|---|---|
| 普通注释 | // 或 /* */ |
代码说明、临时禁用 | ❌ |
| 文档注释 | /// |
为接下来的项生成文档 | ✅ |
| 内部文档注释 | //! |
为父级项(如 crate)生成文档 | ✅ |
1. 普通注释
rust
// 单行注释
/*
多行注释
可以跨越多行
*/
fn main() {
let x = 5; // 行尾注释
// let y = 10; // 可以用注释临时禁用代码
}
2. 文档注释 ///
用于函数、结构体、枚举等,支持 Markdown 格式:
rust
/// 计算两个整数的和
///
/// # Examples
///
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
///
/// # Panics
///
/// 这个函数不会 panic
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
运行 cargo doc --open 可以生成并打开 HTML 文档。
3. 内部文档注释 //!
用于模块或整个 crate 的顶层文档 ,通常放在 lib.rs 或 main.rs 的开头:
rust
//! # 我的计算库
//!
//! 这个库提供了一些基本的数学运算函数。
//!
//! ## 快速开始
//!
//! ```
//! use my_lib::add;
//!
//! let result = add(1, 2);
//! ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
文档注释测试
文档注释中的代码块会自动作为测试运行:
rust
/// 将字符串转换为整数
///
/// # Examples
///
/// ```
/// let result = to_number("42");
/// assert_eq!(result, 42);
/// ```
fn to_number(s: &str) -> i32 {
s.parse().unwrap()
}
运行 cargo test 时会执行这些示例代码。
七、完整代码示例
rust
//! # 函数与注释示例
//!
//! 这个程序演示了 Rust 中的函数定义、返回值、表达式与语句的区别,
//! 以及三种注释风格的使用。
/// 问候函数
///
/// 向指定名称的人打招呼。
///
/// # Arguments
///
/// * `name` - 人的名字
///
/// # Examples
///
/// ```
/// let message = greet("Rust");
/// assert_eq!(message, "Hello, Rust!");
/// ```
fn greet(name: &str) -> String {
format!("Hello, {}!", name) // 隐式返回
}
/// 计算一个数的阶乘(使用循环)
///
/// 返回值是 `n!`,如果 n 为 0 则返回 1。
fn factorial(n: u64) -> u64 {
let mut result = 1;
for i in 1..=n {
result *= i;
}
result
}
/// 用表达式写一个更简洁的阶乘(使用递归)
fn factorial_expr(n: u64) -> u64 {
if n == 0 { 1 } else { n * factorial_expr(n - 1) } // if 是表达式,返回分支的值
}
/// 返回两个值的和与积
fn sum_and_product(a: i32, b: i32) -> (i32, i32) {
(a + b, a * b) // 返回元组
}
fn main() {
// 普通注释:演示函数调用
let greeting = greet("Rust Learner");
println!("{}", greeting);
// 演示阶乘
let n = 5;
println!("{}! = {}", n, factorial(n));
println!("{}! (expr) = {}", n, factorial_expr(n));
// 演示返回元组
let (sum, product) = sum_and_product(10, 20);
println!("10 + 20 = {}, 10 * 20 = {}", sum, product);
// 演示表达式与语句的区别
let expr_result = {
let x = 10;
let y = 20;
x + y // 表达式,值为 30
};
println!("代码块表达式返回: {}", expr_result);
let stmt_result = {
let x = 10;
let y = 20;
x + y; // 语句,返回 ()
};
println!("代码块语句返回: {:?}", stmt_result);
}
八、总结与速查
| 知识点 | 语法 | 示例 |
|---|---|---|
| 定义函数 | fn name(param: Type) -> ReturnType |
fn add(x: i32, y: i32) -> i32 |
| 语句 | 以分号结尾,不返回值 | let x = 5; |
| 表达式 | 不以分号结尾,有值 | 5 + 3、{ let x=1; x+1 } |
| 隐式返回 | 函数体最后一个表达式 | a + b |
| 显式返回 | return value; |
return 42; |
| 无返回值 | -> () 或省略 |
fn do_nothing() |
| 普通注释 | // 或 /* */ |
// 这是一条注释 |
| 文档注释 | /// |
/// 函数说明 |
| 内部文档 | //! |
//! crate 文档 |
核心要点:
- ✅ 函数参数必须声明类型
- ✅ 区分语句(有分号,不返回值)和表达式(无分号,返回值)
- ✅ 隐式返回是 Rust 风格,只有需要早期返回时才用
return - ✅ 用
///写文档注释,用cargo doc生成文档
九、思考题
- 下面代码会输出什么?
rust
fn main() {
let x = {
let y = 42;
println!("y = {}", y);
y
};
println!("x = {}", x);
}
-
如果没有显式返回,函数如何返回多个值?
-
为什么 Rust 要区分语句和表达式?(提示:这和其他语言(如 C/Java)有什么不同?)