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

Rust:函数与控制流

Rust:函数与控制流

    • 条件表达式
      • if分支语句
      • if表达式化
      • if表达式的类型要求
      • match分支匹配
      • match的穷尽性要求
      • match的模式匹配
      • 模式守卫
      • match表达式返回值
    • 循环控制
      • loop无限循环
      • break和continue
      • loop表达式返回值
      • while条件循环
      • while循环中的break
      • for迭代器循环
      • for循环获取索引
      • 标签跳转
    • 函数签名与返回值
      • 基本函数定义
      • 带返回值的函数
      • return
      • 单元类型()
    • 参数传递
      • 数组参数
      • 参数模式匹配
    • 发散函数!
      • 类型兼容性


在编程中,函数和控制流是构建程序逻辑的核心要素。Rust 在这些基础概念上引入了许多独特的设计,比如表达式化的控制结构、严格的类型系统以及所有权机制。本文将从最基础的概念开始,逐步深入探讨 Rust 中的条件表达式、循环控制、函数定义以及各种返回机制。


条件表达式

if分支语句

if 是最基本的条件控制结构,用于根据条件执行不同的代码分支:

let number = 6;if number > 5 {println!("数字大于5");
}if number % 2 == 0 {println!("数字是偶数");
} else {println!("数字是奇数");
}

这是最传统的 if 用法,根据条件执行相应的代码块。当条件为真时,执行 if 后的代码块;当条件为假且有 else 分支时,执行 else 后的代码块。


if表达式化

与其他语言不同,Rust 中的 if 不仅是语句,更是表达式,可以返回值

let number = 6;let result = if number % 2 == 0 {"偶数"
} else {"奇数"
};println!("数字 {} 是 {}", number, result);

这里的关键概念是:代码块 {} 的最后一个表达式就是该代码块的返回值。注意 "偶数""奇数" 后面都没有分号,这意味着它们是表达式而不是语句,会作为代码块的返回值。


if表达式的类型要求

由于 if 是表达式,编译器需要在编译时确定其类型,因此所有分支必须返回相同类型

let condition = true;// 正确:所有分支返回相同类型
let number = if condition {5
} else {6
};println!("number = {}", number);// 错误示例(会编译失败):
let mixed = if condition {5        // 整数类型
} else {"six"    // 字符串类型
};

这个限制确保了类型安全,编译器可以在编译时就确定变量的类型,避免运行时的类型错误。


match分支匹配

match 是 Rust 中更强大的分支控制结构,可以匹配多种模式:

let number = 3;match number {1 => println!("一"),2 => println!("二"),3 => println!("三"),_ => println!("其他"),
}

match 通过模式匹配来决定执行哪个分支。每个分支由模式和对应的代码组成,用 => 连接。_ 是通配符,匹配所有其他情况。


match的穷尽性要求

match 必须覆盖被匹配值的所有可能情况,这被称为"穷尽性"要求:

enum Direction {North,South,East,West,
}let dir = Direction::North;match dir {Direction::North => "向北前进",Direction::South => "向南前进",Direction::East => "向东前进",Direction::West => "向西前进",// 如果缺少任何一个枚举值,编译器会报错
}

穷尽性检查是编译时进行的,确保你不会遗漏任何情况。这大大减少了运行时错误的可能性。


match的模式匹配

match 支持多种复杂的模式匹配:

let number = 7;match number {1 => println!("一"),2 | 3 => println!("二或三"),          // 多值匹配4..=6 => println!("四到六"),          // 范围匹配_ => println!("其他"),
}
  • 多值匹配:使用 | 可以匹配多个值
  • 范围匹配:使用 ..= 可以匹配一个范围内的值

模式守卫

模式守卫允许在匹配模式的基础上添加额外的条件:

let number = 15;match number {n if n > 20 => println!("大于20: {}", n),n if n > 10 => println!("大于10但小于等于20: {}", n),n if n > 0 => println!("大于0但小于等于10: {}", n),0 => println!("恰好是零"),_ => println!("负数或其他"),
}

模式守卫使用 变量 if 条件 的形式,在模式匹配成功后,还要满足 if 后的条件才会执行对应分支,如果不匹配,那么向下继续匹配


match表达式返回值

if 类似,match 也是表达式,可以返回值

let number = 3;let description = match number {1 => "一",2 => "二", 3 => "三",_ => "其他",
};println!("数字 {} 对应 {}", number, description);

所有分支必须返回相同类型的值,这样整个 match 表达式才有确定的类型


循环控制

loop无限循环

loop 创建一个无限循环,是最基本的循环结构:

let mut counter = 0;loop {counter += 1;println!("计数: {}", counter);if counter == 5 {break;}
}println!("循环结束");

loop 会无限执行,只有通过 break 才能退出循环。这是最直接的循环控制方式,适用于需要明确控制退出条件的场景。


break和continue

在循环中,break 用于退出循环,continue 用于跳过本次迭代:

let mut count = 0;loop {count += 1;if count % 2 == 0 {continue;  // 跳过偶数}if count > 10 {break;     // 超过10就退出}println!("奇数: {}", count);
}

continue 会跳过当前循环的剩余代码,直接进入下一次循环break 会立即退出整个循环


loop表达式返回值

loop 也是表达式,可以通过 break 返回值

let mut num = 1;let result = loop {if num % 2 == 0 {break num;  // 返回 num 的值}num += 1;
};println!("第一个偶数: {}", result);

这里 break num 表示退出循环并返回 num 的值。整个 loop 表达式的值就是 break 后面的值。


while条件循环

while 循环在条件为真时持续执行:

let mut number = 5;while number > 0 {println!("倒计时: {}", number);number -= 1;
}println!("发射!");

while 在每次循环开始前检查条件,条件为假时退出循环。这比 loop + if + break 的组合更简洁。


while循环中的break

while 循环中也可以使用 break 提前退出,但与 loop 不同的是,while 循环不允许 break 携带返回值

let mut count = 0;while count < 10 {count += 1;if count == 5 {break;  // 可以 break,但不能 break 值}println!("计数: {}", count);
}let result = while condition { break 42; };  // 错误!while 不能返回值

这是因为 while 循环的条件可能一开始就为假,那样循环体根本不会执行,无法确定返回值


for迭代器循环

for 循环用于遍历集合或迭代器:

let numbers = [1, 2, 3, 4, 5];for num in numbers {println!("数字: {}", num);
}

for 循环会自动遍历可迭代对象的每个元素。这是 Rust 中最常用的循环方式,既安全又高效。


for循环获取索引

有时需要在遍历时获取元素的索引:

let fruits = ["苹果", "香蕉", "橙子"];for (index, fruit) in fruits.iter().enumerate() {println!("{}: {}", index, fruit);
}

enumerate() 方法返回 (索引, 元素) 的元组,让你可以同时访问索引和元素值。


标签跳转

当有嵌套循环时,可以使用标签来指定 breakcontinue 要影响哪个循环:

'outer: loop {println!("进入外层循环");'inner: loop {println!("进入内层循环");break 'outer;  // 跳出外层循环}println!("这行代码不会执行");
}println!("退出所有循环");

标签以单引号开头,可以让 breakcontinue 作用于指定的循环。这在复杂的嵌套循环中很有用。

当使用标签跳转时,break 也可以携带返回值,但只能在 loop 循环中使用

let result = 'outer: loop {let mut inner_count = 0;'inner: loop {inner_count += 1;println!("内层计数: {}", inner_count);if inner_count == 3 {break 'outer inner_count * 10;  // 跳出外层循环并返回值}if inner_count > 5 {break 'inner;  // 只跳出内层循环}}println!("这行代码不会执行");
};println!("返回值: {}", result);  // 输出: 返回值: 30

标签跳转的返回值只能用于 loop 循环,whilefor 循环即使使用标签也不能返回值


函数签名与返回值

基本函数定义

函数是封装代码逻辑的基本单位:

fn greet(name: &str) {println!("Hello, {}!", name);
}greet("World");

函数使用 fn 关键字定义,函数名后跟参数列表,参数需要指定类型


带返回值的函数

函数可以返回值,需要在参数列表后指定返回类型:

fn add(a: i32, b: i32) -> i32 {a + b
}let result = add(5, 3);
println!("5 + 3 = {}", result);

使用 -> 指定返回类型,函数体最后一个表达式作为返回值

对于包含多个分支的函数,每个分支的最后一个表达式都可以作为该分支的返回值

fn calculate(x: i32) -> i32 {let doubled = x * 2;if doubled > 10 {doubled - 5    // 这个分支的返回值} else {doubled + 5    // 这个分支的返回值}// 整个 if 表达式的值作为函数返回值
}fn classify_number(n: i32) -> &'static str {match n {0 => "零",           // 这个分支的返回值1..=10 => "小数",    // 这个分支的返回值11..=100 => "中数",  // 这个分支的返回值_ => "大数",         // 这个分支的返回值}// 整个 match 表达式的值作为函数返回值
}

函数体最后一个表达式(这里是整个 ifmatch 表达式)自动作为返回值,不需要显式的 return


return

虽然 Rust 支持隐式返回,但有时需要在函数中间提前返回:

fn check_positive(x: i32) -> i32 {if x <= 0 {return 0;  // 提前返回}x * 2  // 正常返回
}

return 关键字可以在函数的任何位置提前返回值并退出函数


单元类型()

当函数不需要返回有意义的值时,返回单元类型 ()

fn print_info(msg: &str) {println!("信息: {}", msg);// 隐式返回 ()
}fn do_something() -> () {println!("执行某些操作");// 显式指定返回 (),但通常不需要
}

单元类型 () 表示"无意义的值",类似于其他语言中的 void。不返回值的函数实际上返回 ()


参数传递

函数可以接受参数:

fn display_info(name: &str, age: i32, height: f64) {println!("姓名: {}", name);println!("年龄: {} 岁", age);println!("身高: {:.1} 米", height);
}fn calculate_area(width: f64, height: f64) -> f64 {width * height
}

函数参数按顺序传递,每个参数都必须指定类型


数组参数

可以将数组作为参数传递:

fn print_array(arr: [i32; 5]) {for element in arr {println!("{}", element);}
}fn sum_array(arr: [i32; 3]) -> i32 {let mut total = 0;for element in arr {total += element;}total
}fn main() {let numbers = [1, 2, 3, 4, 5];print_array(numbers);let small_array = [10, 20, 30];let sum = sum_array(small_array);println!("数组总和: {}", sum);
}

数组参数需要指定确切的大小,例如 [i32; 5] 表示包含5个i32元素的数组


参数模式匹配

函数参数可以使用模式匹配直接解构:

// 解构元组参数
fn print_point((x, y): (i32, i32)) {println!("坐标: ({}, {})", x, y);
}// 解构数组的前几个元素
fn print_first_two([first, second, ..]: [i32; 5]) {println!("前两个元素: {} 和 {}", first, second);
}

参数模式匹配让你可以直接在函数签名中解构复杂类型,使代码更简洁。


发散函数!

发散函数是永远不会正常返回的函数,返回类型是 !

fn crash() -> ! {panic!("程序崩溃了");
}fn infinite_loop() -> ! {loop {println!("永远运行...");}
}

! 类型表示"never",意味着函数永远不会正常返回。常见的发散函数包括 panic!、无限循环等。

类型兼容性

一般来说,我们不会把!直接作为返回值,这个类型的特殊之处在于它可以强制转换为任何类型:

fn safe_divide(a: i32, b: i32) -> i32 {if b == 0 {handle_error("除零错误");  // ! 类型,但兼容 i32} else {a / b                      // i32 类型}// 整个 if 表达式的类型是 i32
}

! 类型可以与任何类型兼容,这使得在某些分支中使用 panic!exit 变得很自然,哪怕函数要求返回 i32 等其他类型,你也可以返回一个 ! 来表示一个错误。


http://www.xdnf.cn/news/18788.html

相关文章:

  • 《Java反射与动态代理详解:从原理到实践》
  • 【Ansible】Ansible部署K8s集群--准备环境--配置网络
  • PEFT 模型解析(59)
  • 《数据之心》——鱼小妖的觉醒
  • ctfshow_萌新web16-web20-----文件包含日志注入
  • 《信息检索与论文写作》实验报告二 引文索引数据库检索
  • 我们来学mysql -- safe启动
  • 解析xml文件并录入数据库
  • 类似ant design和element ui的八大Vue的UI框架详解优雅草卓伊凡
  • Vue中的scoped属性
  • 推荐系统王树森(三)粗排精排
  • 【NER学习笔记】:基于AdaSeq的NER模型训练笔记
  • Linux下TCPT通信
  • 8.26 支持向量机
  • 什么样的 IP 能穿越周期,持续被用户买单?​
  • 基于大模型的智能占卜系统实战-Qwen-VL、RAG、FastAPI
  • “喵汪联盟”宠物领养系统的设计与实现(代码+数据库+LW)
  • Python编程快速上手—让繁琐工作自动化
  • OpenCV打开视频函数VideoCapture使用详解
  • 数据与端点安全 (Protect data and apps)
  • 【学习笔记】系统时间跳变会影响time接口解决措施
  • Matlab使用——开发上位机APP,通过串口显示来自单片机的电压电流曲线,实现光伏I-V特性监测的设计
  • es-toolkit 是一个现代的 JavaScript 实用库
  • UE4生成Target文件
  • 【RAGFlow代码详解-11】知识库管理
  • 无人机倾斜摄影农田航线规划
  • ProfiNet 转 Ethernet/IP基于西门子 S7 - 1500 与罗克韦尔 PLC 的汽车零部件加工线协同案例
  • 【QT学习之路】-Qt入门
  • 解决Windows更新后WPF程序报TypeLoadException异常的问题
  • Node.js(3)—— fs模块