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

Rust 的流程控制与函数

流程控制和函数是任何编程语言的核心组成部分,Rust 也不例外。

本章介绍了 Rust 中的流程控制与函数相关知识,包括:

  • 条件判断:if-else结构和if let模式匹配
  • 循环结构:loop(无限循环)、while(条件循环)和for(迭代循环)
  • 函数:定义、参数、返回值和函数指针的使用
  • 函数高级特性:默认参数的模拟、可变参数的实现和递归函数
  • 代码块与作用域:作用域规则和变量遮蔽特性

一、条件判断(if-else、if let)

Rust 的条件判断用于根据不同条件执行不同代码分支,主要包括if-else结构和if let模式匹配。

1.1 if-else 基本用法

Rust 的if条件不需要用括号()包裹,但其代码块必须用{}包围。条件表达式的结果必须是bool类型(Rust 不会自动进行类型转换)。

fn main() {let age = 18;if age >= 18 {println!("已成年");} else if age >= 13 {println!("青少年");} else {println!("儿童");}// 错误示例:条件必须是bool类型// let num = 5;// if num {  // 编译错误:expected `bool`, found `i32`//     println!("num is non-zero");// }
}

if表达式可以作为赋值语句的一部分,因为 Rust 中一切都是表达式(有返回值):

fn main() {let score = 85;let grade = if score >= 90 {'A'} else if score >= 80 {'B'} else {'C'};println!("成绩等级: {}", grade);  // 输出:成绩等级: B
}

1.2 if let 模式匹配

if let是一种简化的模式匹配,适用于只关心一种匹配情况的场景,比match表达式更简洁。常用于处理Option类型或枚举。

fn main() {// 处理Option类型let some_value: Option<i32> = Some(5);// 传统match方式match some_value {Some(x) => println!("找到值: {}", x),None => (),  // 不关心None的情况,只做占位}// 等价的if let方式if let Some(x) = some_value {println!("找到值: {}", x);  // 输出:找到值: 5}// 结合else处理不匹配的情况let another_value: Option<i32> = None;if let Some(x) = another_value {println!("找到值: {}", x);} else {println!("没有值");  // 输出:没有值}// 处理枚举enum Fruit {Apple,Banana(String),}let fruit = Fruit::Banana("黄香蕉".to_string());if let Fruit::Banana(color) = fruit {println!("这是{}的香蕉", color);  // 输出:这是黄香蕉的香蕉}
}

二、循环结构(loop、while、for 与迭代器)

Rust 提供了三种循环结构:loop(无限循环)、while(条件循环)和for(迭代循环),其中for循环与迭代器结合使用最为常见。

2.1 loop 无限循环

loop创建一个无限循环,必须使用break才能退出。它的特殊之处是可以返回一个值。

fn main() {// 基本用法let mut count = 0;loop {count += 1;if count == 3 {println!("计数到3,退出循环");  // 输出:计数到3,退出循环break;  // 退出循环}}// 带返回值的looplet mut sum = 0;let result = loop {sum += 5;if sum >= 20 {break sum;  // 返回sum的值}};println!("循环返回值: {}", result);  // 输出:循环返回值: 20
}

2.2 while 条件循环

while循环在条件为true时持续执行,适合不确定循环次数的场景。

fn main() {let mut number = 3;while number > 0 {println!("{}!", number);number -= 1;}println!("发射!");// 输出:// 3!// 2!// 1!// 发射!
}

2.3 for 循环与迭代器

for循环是 Rust 中最常用的循环方式,通常与迭代器配合使用,用于遍历集合或范围。

fn main() {// 遍历范围(左闭右开)for i in 1..5 {  // 1到4(不包含5)println!("{}", i);  // 输出:1 2 3 4}// 遍历数组let fruits = ["苹果", "香蕉", "橙子"];for fruit in fruits {println!("水果: {}", fruit);}// 使用迭代器方法enumerate获取索引和值for (index, value) in fruits.iter().enumerate() {println!("索引{}: {}", index, value);// 输出:// 索引0: 苹果// 索引1: 香蕉// 索引2: 橙子}// 遍历集合(需要引入标准库)use std::collections::VecDeque;let mut queue = VecDeque::new();queue.push_back("a");queue.push_back("b");for item in queue {println!("队列元素: {}", item);}
}

Rust 的迭代器提供了丰富的方法(如mapfiltercollect等),可以进行复杂的数据处理:

fn main() {let numbers = 1..10;// 过滤偶数并乘以2let result: Vec<i32> = numbers.filter(|x| x % 2 == 0).map(|x| x * 2).collect();println!("处理结果: {:?}", result);  // 输出:处理结果: [4, 8, 12, 16]
}

三、函数定义与调用(参数、返回值、函数指针)

函数是代码复用和逻辑组织的基本单元,Rust 中使用fn关键字定义函数。

3.1 函数定义与调用

Rust 函数的基本结构:fn 函数名(参数列表) -> 返回值类型 { 函数体 }

// 定义一个简单函数
fn greet(name: &str) {println!("Hello, {}!", name);
}// 带返回值的函数
fn add(a: i32, b: i32) -> i32 {a + b  // 表达式末尾没有分号,作为返回值
}// 多个返回值(通过元组实现)
fn split_name(full_name: &str) -> (&str, &str) {let parts: Vec<&str> = full_name.split_whitespace().collect();(parts[0], parts[1])  // 返回元组
}fn main() {// 调用函数greet("Rust");  // 输出:Hello, Rust!let sum = add(3, 5);println!("3 + 5 = {}", sum);  // 输出:3 + 5 = 8let (first, last) = split_name("John Doe");println!("名: {}, 姓: {}", first, last);  // 输出:名: John, 姓: Doe
}

注意:

函数参数必须指定类型
函数返回值通过->指定类型
函数体中最后一个表达式(不带分号)作为返回值,也可以使用return关键字提前返回

3.2 函数参数的可变性

Rust 函数参数默认是不可变的,若要在函数内修改参数,需要显式声明mut

fn main() {let mut x = 5;modify_value(&mut x);  // 传递可变引用println!("修改后的值: {}", x);  // 输出:修改后的值: 10
}// 接收可变引用参数
fn modify_value(num: &mut i32) {*num *= 2;  // 使用*解引用
}

3.3 函数指针

函数指针(function pointer)允许将函数作为参数传递或存储在变量中,类型表示为fn(参数类型) -> 返回值类型

// 定义几个数学函数
fn add(a: i32, b: i32) -> i32 {a + b
}fn subtract(a: i32, b: i32) -> i32 {a - b
}// 接收函数指针作为参数
fn calculate(operation: fn(i32, i32) -> i32, x: i32, y: i32) -> i32 {operation(x, y)
}fn main() {let a = 10;let b = 5;// 传递函数作为参数let sum = calculate(add, a, b);let diff = calculate(subtract, a, b);println!("{} + {} = {}", a, b, sum);  // 输出:10 + 5 = 15println!("{} - {} = {}", a, b, diff);  // 输出:10 - 5 = 5// 存储函数指针let op: fn(i32, i32) -> i32 = add;println!("使用函数指针: {}", op(3, 4));  // 输出:7
}

四、函数的高级特性(默认参数、可变参数、递归)

Rust 的函数系统提供了一些高级特性,虽然有些特性的实现方式与其他语言不同,但能满足复杂的编程需求。

4.1 默认参数

Rust没有内置的默认参数语法,但可以通过以下方式模拟:

使用结构体 + 默认实现

// 定义配置结构体
#[derive(Default)]
struct Config {timeout: u32,retries: u8,
}// 接收配置参数的函数
fn connect(config: Config) {println!("连接配置 - 超时: {}ms, 重试次数: {}", config.timeout, config.retries);
}fn main() {// 使用默认配置connect(Config::default());  // 输出:连接配置 - 超时: 0ms, 重试次数: 0// 自定义部分配置connect(Config {timeout: 5000,..Config::default()  // 其余使用默认值});  // 输出:连接配置 - 超时: 5000ms, 重试次数: 0
}

使用函数重载

// 不使用 derive(Default),而是手动实现
struct Config {timeout: u32,retries: u8,
}// 手动实现 Default trait,定义自定义默认值
impl Default for Config {fn default() -> Self {Config {timeout: 5000,  // 自定义默认超时为 5000msretries: 3,     // 自定义默认重试次数为 3 次}}
}// 接收配置参数的函数
fn connect(config: Config) {println!("连接配置 - 超时: {}ms, 重试次数: {}", config.timeout, config.retries);
}fn main() {// 使用默认配置connect(Config::default());  // 输出:连接配置 - 超时: 0ms, 重试次数: 0// 自定义部分配置connect(Config {timeout: 1000,..Config::default()  // 其余使用默认值});  // 输出:连接配置 - 超时: 5000ms, 重试次数: 0
}

4.2 定义接口

在 Rust 中,trait(特质)是一种定义方法集合的机制,类似于其他语言中的 “接口(interface)”,但功能更灵活。它主要用于:

  1. 定义一组行为(方法)的规范,要求实现者必须提供这些行为的具体逻辑;
  2. 实现代码复用和多态(同一接口的不同实现)。

通俗理解:trait 是 “行为规范”

可以把 trait 想象成一份 “协议”,它规定了 “实现者必须具备哪些能力(方法)”。例如,Greet trait 定义了 “打招呼” 的能力,任何类型(结构体、枚举等)只要只要实现 Greet,就必须提供 greet 方法的具体实现。

基本用法示例

// 定义一个 trait(行为规范):要求实现者必须有 greet 方法
trait Greet {// 声明方法(只定义签名,不写实现)fn greet(&self);
}// 定义一个结构体(实现者1)
struct Person {name: String,
}// 为 Person 实现 Greet trait(遵守规范)
impl Greet for Person {fn greet(&self) {println!("Hello, I'm {}", self.name);}
}// 定义另一个结构体(实现者2)
struct Robot {model: String,
}// 为 Robot 实现 Greet trait(不同实现)
impl Greet for Robot {fn greet(&self) {println!("Greetings, human. I am {}", self.model);}
}fn main() {let alice = Person { name: "Alice".to_string() };let r2d2 = Robot { model: "R2-D2".to_string() };// 调用各自的 greet 方法(多态:同一方法名,不同实现)alice.greet();  // 输出:Hello, I'm Alicer2d2.greet();   // 输出:Greetings, human. I am R2-D2
}

trait 的核心特性

1.强制实现

任何类型要使用 trait,必须实现其所有方法(除非方法有默认实现)。

2.默认方法

可以在 trait 中直接提供方法的默认实现,实现者可以选择覆盖或直接使用:

trait Greet {// 带默认实现的方法fn greet(&self) {println!("Hello!");}
}struct Cat;
struct Dog;
// 不覆盖默认方法,直接使用
impl Greet for Cat {}
// 覆盖默认方法
impl Greet for Dog {fn greet(&self) {println!("Hello! Dog!");}
}
fn main() {Cat.greet();  // 输出:Hello!Dog.greet();  // 输出:Hello! Dog!
}
3. 规范参数 / 返回值

可以用 trait 约束函数的参数或返回值,实现 “泛型多态”:

// 定义一个 trait(行为规范):要求实现者必须有 greet 方法
trait Greet {// 声明方法(只定义签名,不写实现)fn greet(&self);
}// 定义一个结构体(实现者1)
struct Person {name: String,
}// 为 Person 实现 Greet trait(遵守规范)
impl Greet for Person {fn greet(&self) {println!("Hello, I'm {}", self.name);}
}// 定义另一个结构体(实现者2)
struct Robot {model: String,
}// 为 Robot 实现 Greet trait(不同实现)
impl Greet for Robot {fn greet(&self) {println!("Greetings, human. I am {}", self.model);}
}// 接受任何实现了 Greet 的类型
fn say_hello(who: &impl Greet) {who.greet();
}fn main() {let alice = Person { name: "Alice".to_string() };let r2d2 = Robot { model: "R2-D2".to_string() };// 调用各自的 greet 方法(多态:同一方法名,不同实现)alice.greet();  // 输出:Hello, I'm Alicer2d2.greet();   // 输出:Greetings, human. I am R2-D2say_hello(&alice);  // 合法(Person 实现了 Greet)say_hello(&r2d2);   // 合法(Robot 实现了 Greet)
}
4. 与 “接口” 的区别

虽然类似其他语言的接口,trait 有更灵活的特性:

  • 可以为任何类型实现 trait(包括标准库类型,如 i32String 等);
  • 支持 “默认方法”,减少重复代码;
  • 可以通过 trait 组合实现复杂行为。

总之,trait 是 Rust 中定义行为规范的核心机制,它:

  • 规定了类型必须实现的方法;
  • 支持多态和代码复用;
  • 是 Rust 实现 “接口抽象” 的主要方式。

通过 trait,你可以写出更灵活、可扩展的代码,尤其是在需要处理多种类型但统一行为的场景(如通用算法、工具函数等)。

4.3 可变参数

Rust 支持可变参数(variable arguments),通常通过宏或标准库中的std::fmt::Arguments实现:

1. 使用宏定义可变参数函数

// 定义可变参数宏
macro_rules! sum {// 递归终止条件($x:expr) => ($x);// 递归处理多个参数($x:expr, $($y:expr),+) => ($x + sum!($($y),+));
}fn main() {let s1 = sum!(1, 2, 3);let s2 = sum!(10, 20, 30, 40);println!("sum1: {}, sum2: {}", s1, s2);  // 输出:sum1: 6, sum2: 100
}

2. 使用std::fmt模块处理可变参数(类似println!

use std::fmt;fn log(fmt: fmt::Arguments) {println!("[LOG] {}", fmt);
}fn main() {let user = "admin";let action = "login";// 使用format_args!创建可变参数log(format_args!("用户 {} 执行了 {} 操作", user, action));// 输出:[LOG] 用户 admin 执行了 login 操作
}

4.4 递归函数

递归函数是调用自身的函数,在 Rust 中使用递归需要注意:

  • 必须有明确的终止条件
  • Rust 目前不支持尾递归优化,递归深度过大会导致栈溢出
// 计算阶乘(n! = n × (n-1) × ... × 1)
fn factorial(n: u64) -> u64 {// 终止条件if n <= 0 {1} else {n * factorial(n - 1)  // 递归调用}
}// 斐波那契数列(F(n) = F(n-1) + F(n-2))
fn fibonacci(n: u32) -> u32 {match n {0 => 0,1 => 1,_ => fibonacci(n - 1) + fibonacci(n - 2),  // 递归调用}
}fn main() {println!("5的阶乘: {}", factorial(5));  // 输出:5的阶乘: 120println!("斐波那契数列第10项: {}", fibonacci(10));  // 输出:55
}

对于深度较大的递归,建议使用循环替代,避免栈溢出:

// 用循环实现阶乘(更安全)
fn factorial_iterative(n: u64) -> u64 {let mut result = 1;for i in 1..=n {result *= i;}result
}fn main() {println!("5的阶乘: {}", factorial_iterative(5));  // 输出:5的阶乘: 120
}

五、代码块与作用域({} 作用域、变量遮蔽 Shadowing)

Rust 通过代码块(block)和作用域(scope)管理变量的生命周期,变量遮蔽是 Rust 中一个有特色的概念。

5.1 代码块与作用域

代码块用{}包围,创建一个新的作用域。变量在声明的作用域内有效,出了作用域会被自动释放(RAII 机制)。

fn main() {// 外部作用域let x = 10;println!("外部作用域的x: {}", x);  // 输出:外部作用域的x: 10{// 内部作用域let y = 20;println!("内部作用域的y: {}", y);  // 输出:内部作用域的y: 20println!("内部作用域可以访问外部变量x: {}", x);  // 输出:10}// 错误:y的作用域已结束// println!("外部作用域无法访问y: {}", y);  // 编译错误// 控制流结构(if/loop/for等)也会创建作用域if x > 5 {let z = 30;println!("if块内的z: {}", z);  // 输出:if块内的z: 30}// 错误:z的作用域已结束// println!("if块外无法访问z: {}", z);  // 编译错误
}

5.2 变量遮蔽(Shadowing)

变量遮蔽允许在同一作用域或内层作用域中用相同名称声明新变量,新变量会 "遮蔽" 旧变量。

fn main() {let x = 5;println!("x = {}", x);  // 输出:x = 5// 遮蔽:同一作用域中创建同名新变量let x = x + 1;println!("x = {}", x);  // 输出:x = 6// 遮蔽:改变变量类型let x = "现在我是字符串";println!("x = {}", x);  // 输出:x = 现在我是字符串// 内层作用域遮蔽外层变量{let x = 100;println!("内层作用域的x = {}", x);  // 输出:内层作用域的x = 100}// 内层作用域结束后,外层变量恢复可见println!("外层作用域的x = {}", x);  // 输出:外层作用域的x = 现在我是字符串
}

变量遮蔽的主要用途:

  • 转换变量类型同时保持名称一致
  • 临时修改变量值但不影响外部作用域
  • 简化代码(无需为相关变量创建不同名称)

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

    相关文章:

  • SQL 中 DISTINCT 的全方位指南:从基础用法到性能优化
  • 【51单片机】【protues仿真】基于51单片机温度烟雾控制系统
  • C++项目实战——高性能内存池(一)
  • Redis面试精讲 Day 26:Redis源码分析:事件循环与网络模型
  • docker使用和部署深化学习
  • 深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第一章知识点问答(21题)
  • 华为AUTOSAR质量目标与开发实践
  • LeetCode100 -- Day3
  • 常德二院全栈国产化实践:KingbaseES 数据库的关键作用
  • 机器学习聚类算法学习报告
  • Spring容器Bean的创建流程
  • 使用jwt+redis实现单点登录
  • Matplotlib+HTML+JS:打造可交互的动态数据仪表盘
  • Android - 统一资源标识符 Uri
  • 利用DeepSeek编写调用系统命令用正则表达式替换文件中文本的程序
  • [NCTF2019]True XML cookbook
  • PyTorch Vision 系列:高效数据处理的利器
  • 动手学深度学习(pytorch版):第五章节—深度学习计算(2)参数管理
  • 进程和进程调度
  • Rclone入门对象存储云到云迁移
  • 我从零开始学微积分(2)- 函数与图形
  • YOLO --- YOLOv3以及YOLOv4模型详解
  • Redis Hash数据类型深度解析:从命令、原理到实战场景
  • IPSEC安全基础后篇
  • 易焓仪器安全帽耐熔融金属飞溅性能测试仪:飞溅场景适配与精准检测
  • 力扣 30 天 JavaScript 挑战 第37天 第九题笔记 知识点: 剩余参数,拓展运算符
  • 智慧农业温室大棚远程监控物联网系统解决方案
  • CRaxsRat v7.4:网络安全视角下的深度解析与防护建议
  • AECS(国标ECALL GB 45672-2025)
  • 5G视频终端详解 无人机图传 无线图传 便携式5G单兵图传