Rust 学习(4)-函数与注释

文章目录

返回首页 | 上篇 | 下篇


掌握 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 + 3x + 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.rsmain.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 生成文档

九、思考题

  1. 下面代码会输出什么?
rust 复制代码
fn main() {
    let x = {
        let y = 42;
        println!("y = {}", y);
        y
    };
    println!("x = {}", x);
}
  1. 如果没有显式返回,函数如何返回多个值?

  2. 为什么 Rust 要区分语句和表达式?(提示:这和其他语言(如 C/Java)有什么不同?)


参考链接

返回首页 | 上篇 | 下篇

相关推荐
tiana_2 小时前
写了个零依赖的 Go 版本管理器,curl | bash 完事
开发语言·golang·bash
嘘嘘出差2 小时前
Python 爬虫入门实战:爬取豆瓣电影 Top 250
开发语言·爬虫·python
猪头少年不会遇见算法学姐2 小时前
Makefile快速上手笔记
linux·学习
爱写代码的倒霉蛋2 小时前
实现协程的三种方式
开发语言·python
崖边看雾2 小时前
记录Python学习——第一章环境安装下载(Windows)
windows·python·学习·pycharm
CS创新实验室2 小时前
NumPy数组的C风格和Fortran风格
c语言·开发语言·numpy
dankokoko3 小时前
7.13-7.14学习
学习
逝水无殇3 小时前
C# 正则表达式详解
开发语言·后端·正则表达式·c#
朱永博3 小时前
使用Onnruntime实现Padim/Patchcore推理
开发语言·c#