当前位置: 首页 > news >正文

【Rust】 4. 函数与闭包

第一部分:函数 (Functions)

  1. 函数简介

函数是 Rust 中封装可重用代码的基本构建块,使用 fn 关键字定义。

定义语法:


fn function_name(parameter1: Type1, parameter2: Type2, ...) -> ReturnType {// 函数体// 可以包含多条语句和表达式// 可以使用 return 语句返回值
}
  • fn:定义函数的关键字。

  • function_name:函数名,遵循 snake_case 命名规范(全小写,下划线分隔)。

  • parameter: Type:参数列表,每个参数都必须显式注明类型。

  • -> ReturnType:指定函数的返回类型。

  • 函数体:由 {} 包围,包含实际的执行逻辑。

核心特性:

  • 函数返回值可以通过 return 语句显式指定,也可以通过省略最后一个表达式末尾的分号来隐式返回。

  • 若未指定返回类型,编译器默认返回单元类型 ()。

  • 程序入口是 fn main()。

  • Rust 不关心函数定义的位置(可在 main 之前或之后),只要在作用域内定义了即可调用。

  • 函数体内可以定义其他项(如静态变量、常量、嵌套函数、trait、类型等)。

  1. 函数实例

fn main() {let num = add(1, 2); // 调用函数println!("{}", num) // 输出: 3
}fn add(x: i32, y: i32) -> i32 {x + y // 这是一个表达式,其值被隐式返回
}
  1. 参数与返回值
  • 参数:函数可以接受零个或多个强类型参数。

  • 返回值:函数可以返回一个值,或不返回任何值(即返回 ())。

  • 语句 vs 表达式:这是理解返回值的核心。

    • 语句:以分号 ; 结尾,执行操作但不返回值(类型为 ())。
    • 表达式:不以分号结尾,会计算并产生一个值。

错误示例:


fn add(x: i32, y: i32) -> i32 {x + y; // 错误!加了分号,这变成了语句,返回的是 (),与声明的返回类型 i32 冲突
}
// 正确做法是去掉分号:`x + y`
  1. 参数传递方式

Rust 支持两种参数传递方式:

  • 按值传递 (Pass by Value)

    • 函数获得参数的副本。

    • 在函数内部修改副本不会影响原始值。

    • 适用于实现 Copy trait 的类型(如基本整数、布尔、字符等)。

  • 按引用传递 (Pass by Reference)

    • 函数获得参数的引用(指针),而非值本身。

    • 不可变引用 (&T):允许函数读取但不能修改原始值。

    • 可变引用 (&mut T):允许函数读取和修改原始值。

    • 适用于所有类型,尤其是不想转移所有权或数据较大时。

示例:


fn increment_by_value(mut num: i32) { // 按值传递,获取副本num += 1; // 修改副本println!("Result: {}", result); // 输出: 6
}fn increment_by_reference(num: &mut i32) { // 按可变引用传递*num += 1; // 解引用并修改原始值
}fn main() {let mut value = 5;increment_by_value(value); // 传递值的副本println!("Original value: {}", value); // 输出: 5 (原始值未变)increment_by_reference(&mut value); // 传递可变引用println!("Updated value: {}", value); // 输出: 6 (原始值被修改)
}
  1. 函数作为值与高阶函数 (Higher-Order Functions)

在 Rust 中,函数是一等公民(First-class citizens),可以像普通值一样被赋值、传递和返回。

  • 函数指针类型:fn(i32, i32) -> i32

  • 高阶函数:指那些以函数为参数和/或返回函数的函数。

示例:


fn add(a: i32, b: i32) -> i32 { a + b }
fn subtract(a: i32, b: i32) -> i32 { a - b }// 高阶函数:接受一个函数指针 `operation` 作为参数
fn calculate(operation: fn(i32, i32) -> i32, a: i32, b: i32) -> i32 {operation(a, b) // 调用传入的函数
}fn main() {let result1 = calculate(add, 5, 3); // 传递函数 `add`println!("Result1: {}", result1); // 输出: 8let result2 = calculate(subtract, 8, 4); // 传递函数 `subtract`println!("Result2: {}", result2); // 输出: 4
}
  1. 泛型函数 (Generic Functions)

泛型函数允许编写适用于多种类型的通用代码,使用类型参数(通常用 表示)实现。

示例:


use std::fmt::Display;// <T: Display> 表示类型 T 必须实现 Display trait
fn print_type<T: Display>(value: T) {println!("Type: {}", std::any::type_name::<T>()); // 打印类型名println!("Value: {}", value); // 打印值
}fn main() {print_type(5); // T 是 i32print_type("Hello"); // T 是 &str
}
// 输出:
// Type: i32
// Value: 5
// Type: &str
// Value: Hello
  1. 发散函数 (Diverging Functions)

发散函数永远不会返回到调用者。其返回类型标记为 !(never type)。

常见场景:

  • panic! 宏及其变体(如 unimplemented!, unreachable!)。

  • 无限循环 loop {}。

  • 进程退出函数(如 std::process::exit)。

示例:


fn diverges() -> ! {panic!("This function will never return!");
}

特点: 发散表达式可以被转换为任何类型,这在需要统一类型的场合(如 match 表达式的不同分支)很有用。
8. 常量函数 (const fn)

使用 const 关键字修饰的函数可以在编译时被求值,其结果可作为常量使用。

  • 限制:const fn 的函数体内只能使用有限的编译时可确定的操作。

  • 状态:此功能仍在发展和完善中。

示例:


const fn square(x: i32) -> i32 {x * x
}const CONST_SQUARE: i32 = square(5); // 编译时计算

第二部分:闭包 (Closures)

  1. 闭包简介

闭包是匿名函数,可以捕获其定义所在作用域中的变量。它们非常灵活,可以像普通值一样传递和存储。
2. 基本语法与演变

闭包通常定义为 |args| expression 的形式。

演变过程:


// 1. 标准函数
fn add_one(x: i32) -> i32 { x + 1 }// 2. 闭包 (完整写法)
let add_one_v1 = |x: i32| -> i32 { x + 1 };// 3. 闭包 (省略返回类型,编译器推断)
let add_one_v2 = |x: i32| { x + 1 };// 4. 闭包 (省略大括号,单表达式)
let add_one_v3 = |x| x + 1; // 类型也可由调用处推断// 使用
println!("{}", add_one_v3(5)); // 输出: 6
  1. 捕获环境变量

这是闭包与函数最核心的区别:闭包可以捕获并使用其定义作用域内的变量。

闭包可以:


fn main() {let x = 10;let add = |y| x + y; // 捕获了外部变量 `x`println!("{}", add(5)); // 输出: 15
}

函数不可以:


fn main() {let x = 10;fn add(y: i32) -> i32 {x + y // 错误!函数无法捕获环境变量 `x`}
}
  1. 捕获方式与闭包 Trait

Rust 闭包根据其如何使用捕获的变量,会自动实现以下三种 Trait 之一:

Trait捕获方式是否可变描述示例
Fn不可变借用(&T)不可变只读取捕获变量的值,不修改它们。最常见。let c = |x| x + captured_var;
FnMut可变借用(&mut T)可变可以修改捕获的变量。let mut c = |x { *captured_var += x; };
FnOnce获取所有权(T)消耗会消耗(移动)捕获的变量,只能调用一次。let c = |x| { drop(captured_var); };
  • move 关键字:强制闭包通过值(获取所有权)来捕获变量,通常用于将闭包传递到新线程时避免生命周期问题。
let s = String::from("hello");let f = move || println!("{}", s); // `s` 被移动进闭包,所有权不再在主函数中// println!("{}", s); // 错误!s 的所有权已移入闭包f();
  • 编译器会自动为闭包选择最合适的 Trait,实现静态分发。
  1. 闭包 vs 函数
项目函数闭包
是否有名字有 (fn 定义)无(匿名)
捕获外部变量❌ 不能✅ 可以
类型函数指针 (fn())匿名结构体,实现 Fn/FnMut/FnOnce
类型标注必须显式注明参数和返回类型通常可省略,由编译器推断
存储代码段通常存储在栈上(可能捕获数据)

总结: 闭包提供了比函数更大的灵活性,特别是在需要捕获上下文和进行函数式编程的场景中。函数则更简单、更明确,适用于不需要捕获环境的通用逻辑。

http://www.dtcms.com/a/354439.html

相关文章:

  • React过渡更新:优化渲染性能的秘密
  • 在Excel和WPS表格中隔一行插入一个空白行
  • HarmonyOS 中的 sharedTransition:实现流畅的页面过渡动画
  • 从数字到价值:ESG评级的深层变革
  • 鸿蒙 5.1 深度解析:ArkUI 4.1 升级与分布式开发新范式
  • Linux 软件编程(十三)网络编程:TCP 并发服务器模型与 IO 多路复用机制、原理epoll
  • 【Windows】netstat命令解析及端口状态解释
  • 【PostgreSQL内核学习:通过 ExprState 提升哈希聚合与子计划执行效率(二)】
  • 现代前端状态管理:从原理到实战(Vue/React全栈方案)
  • 【自记】Python 中,对象的比较运算符(>, ==, <=, >=)对应特定的魔法方法详解
  • H5测试全攻略:要点解析
  • 一个工程多Module的微服务项目,如何在GitLab中配置CI/CD
  • MySQL数据库精研之旅第十三期:吃透用户与权限管理,筑牢数据库安全第一道防线
  • 深入解析Java并发编程与单例模式
  • 详解Log4j组件:工业级Java日志框架
  • Redis实战-点赞的解决方案
  • vue布局
  • LightGBM 在金融逾期天数预测任务中的经验总结
  • 2025年渗透测试面试题总结-36(题目+回答)
  • 2025年渗透测试面试题总结-37(题目+回答)
  • vue3 数据库 内的 字符 显示 换行符
  • LeetCode-238除自身以外数组的乘积
  • 基于单片机步进电机控制电机正反转加减速系统Proteus仿真(含全部资料)
  • codeforces(1045)(div2) E. Power Boxes
  • 2024年09月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • Kubernetes 的20 个核心命令分类详解
  • 深度学习11 Deep Reinforcement Learning
  • 基于视觉的网页浏览Langraph Agent
  • 【RAG知识库实践】向量数据库VectorDB
  • Linux应用软件编程---网络编程(TCP并发服务器构建:[ 多进程、多线程、select ])