Rust 学习笔记:函数和控制流

Rust 学习笔记:函数和控制流

  • Rust 学习笔记:函数和控制流
    • 函数(Function)
    • 语句和表达式
    • 带返回值的函数
    • 注释
    • 控制流
      • if 表达式
      • 使用 else if 处理多个条件
      • 在 let 语句中使用 if
      • 循环
        • loop
        • 从循环中返回值
        • 循环标签消除多个循环之间的歧义
        • 带 while 的条件循环
        • 使用 for 循环遍历集合

Rust 学习笔记:函数和控制流

本篇文章介绍 Rust 的函数、注释和控制流。

函数(Function)

fn 关键字允许声明新函数。main 函数是程序的入口点。

在 Rust 中定义函数时,输入 fn,后跟函数名和一组圆括号。花括号告诉编译器函数体的开始和结束位置。

Rust 并不关心你在哪里定义你的函数,只关心它们被定义在调用者可以看到的作用域中。

示例:

fn main() {println!("Hello, world!");another_function();
}fn another_function() {println!("Another function.");
}

运行结果:

在这里插入图片描述

我们可以将函数定义为具有形参的函数,形参是作为函数签名一部分的特殊变量。

示例:

fn main() {another_function(5);
}fn another_function(x: i32) {println!("The value of x is: {x}");
}

运行结果:

在这里插入图片描述

在函数定义中,必须声明每个参数的类型。当定义多个参数时,用逗号分隔参数声明。

示例:

fn main() {print_labeled_measurement(5, 'h');
}fn print_labeled_measurement(value: i32, unit_label: char) {println!("The measurement is: {value}{unit_label}");
}

运行结果:

在这里插入图片描述

语句和表达式

在 Rust 中,函数体由一系列语句组成,可以由表达式结尾。

  • 语句是执行某些操作的指令,但不返回值。
  • 表达式计算并返回一个结果值。

函数定义也是语句。

创建变量并使用 let 关键字为其赋值是语句。因为语句不返回值,所以不能将 let 语句赋值给另一个变量。

示例:

fn main() {let x = (let y = 6);
}

运行报错:

在这里插入图片描述

let y = 6语句不返回值,因此没有任何东西可以绑定 x。这与其他语言(如 C)中的情况不同,在这些语言中,赋值返回赋值的值。在这些语言中,你可以写 x = y = 6,让 x 和 y 的值都是 6,但在 Rust 中却不是这样。

表达式的计算结果是一个值,并且构成了在 Rust 中编写的大部分其余代码。考虑一个数学运算,比如 5 + 6,它是一个计算值为 11 的表达式。表达式可以是语句的一部分,例如语句 let y = 6 中的 6 是求值为 6 的表达式。调用函数是一个表达式。调用宏是一个表达式。用大括号创建的新作用域块是一个表达式。

示例:

fn main() {let y = {let x = 3;x + 1};println!("The value of y is: {y}");
}

表达式

{let x = 3;x + 1
}

是一个块,在这个例子中,它的值是 4。作为 let 语句的一部分,这个值被绑定到 y 上。注意,x + 1 行末尾没有分号,这与目前看到的大多数行不同。

表达式不包括结束分号。如果在表达式的末尾添加分号,则将其转换为语句,然后它将不返回值。

带返回值的函数

函数可以向调用它们的代码返回值。我们不为返回值命名,但必须在箭头(->)后面声明它们的类型。在 Rust 中,函数的返回值与函数体块中最终表达式的值是同义的。通过使用 return 关键字并指定一个值,可以提前从函数返回,但是大多数函数隐式地返回最后一个表达式。下面是一个返回值的函数示例:

示例:

fn five() -> i32 {5
}fn main() {let x = five();println!("The value of x is: {x}");
}

在 five 函数中没有函数调用、宏,甚至没有 let 语句——只有数字 5 本身。这在 Rust 中是一个完全有效的函数。注意,函数的返回类型也被指定为 i32。

另一个示例:

fn main() {let x = plus_one(5);println!("The value of x is: {x}");
}fn plus_one(x: i32) -> i32 {x + 1
}

运行这段代码将打印 x 的值为 6。但是,如果我们在包含 x + 1 的行末尾放置一个分号,将其从表达式更改为语句,程序将报错。

注释

在 Rust 中,惯用的注释样式以两个斜杠开始注释,并且注释一直持续到行尾。对于超出单行的注释,你需要在每一行都包含 //,像这样:

// So we’re doing something complicated here, long enough that we need
// multiple lines of comments to do it! Whew! Hopefully, this comment will
// explain what’s going on.

注释可以放在它所注释的代码上方的单独一行,也可以放在包含以下代码的行尾。

fn main() {// I’m feeling lucky todaylet lucky_number = 7; // I’m feeling lucky today
}

控制流

在大多数编程语言中,根据条件是否为真来运行某些代码以及在条件为真时重复运行某些代码的能力是基本的构建块。Rust 代码执行流的最常见结构是 if 表达式和循环。

if 表达式

if 表达式允许根据条件对代码进行分支。提供一个条件,然后声明:“如果满足此条件,则运行此代码块;如果条件不满足,就不要运行这段代码。”

示例:

fn main() {let number = 3;if number < 5 {println!("condition was true");} else {println!("condition was false");}
}

运行结果:

在这里插入图片描述

同样值得注意的是,这段代码中的条件必须是 bool 类型。如果条件不是 bool 类型,我们将得到一个错误。例如,尝试运行以下代码:

fn main() {let number = 3;if number {println!("number was three");}
}

报错:

在这里插入图片描述

这个错误表明 Rust 期望的是 bool 值,但得到的却是整数。Rust 不会自动尝试将非布尔类型转换为布尔类型,必须明确地提供一个布尔值作为它的条件。

使用 else if 处理多个条件

可以通过在 else if 表达式中组合 if 和 else 来使用多个条件。

示例:

fn main() {let number = 6;if number % 4 == 0 {println!("number is divisible by 4");} else if number % 3 == 0 {println!("number is divisible by 3");} else if number % 2 == 0 {println!("number is divisible by 2");} else {println!("number is not divisible by 4, 3, or 2");}
}

当这个程序执行时,它依次检查每个 if 表达式,并执行条件为 true 的第一个语句体。

在 let 语句中使用 if

因为 if 是一个表达式,我们可以在 let 语句的右侧使用它将结果赋值给一个变量。

正确的示例:

fn main() {let condition = true;let number = if condition { 5 } else { 6 };println!("The value of number is: {number}");
}

有可能成为 if 的每个分支的结果的值必须是相同的类型,如果类型不匹配,将会报错。

错误的示例:

fn main() {let condition = true;let number = if condition { 5 } else { "six" };println!("The value of number is: {number}");
}

报错信息:

在这里插入图片描述

if 块中的表达式求值为整数,else 块中的表达式求值为字符串。这行不通,因为变量必须有单一类型,Rust 需要在编译时明确地知道 number 变量是什么类型。

循环

Rust 有三种循环:loop、while 和 for。我们每个都试试。

loop

loop 关键字将无限循环,直到显式停止程序。

示例:

fn main() {loop {println!("again!");}
}

大多数终端都支持键盘快捷键 Ctrl+C 来中断陷入连续循环的程序。

幸运的是,Rust 还提供了一种使用代码跳出循环的方法。可以在循环中放置 break 关键字,以告诉程序何时停止执行循环。

continue 关键字则在循环中告诉程序跳过此循环迭代中的任何剩余代码,并进入下一个迭代。

从循环中返回值

可以在用于停止循环的 break 表达式之后添加想要返回的值,该值将从循环中返回。

示例:

fn main() {let mut counter = 0;let result = loop {counter += 1;if counter == 10 {break counter * 2;}};println!("The result is {result}");
}

运行结果:

在这里插入图片描述

在循环之前,声明一个名为 counter 的变量,并将其初始化为 0。然后声明一个名为 result 的变量来保存循环返回的值。在循环的每次迭代中,我们给计数器变量加 1,然后检查计数器是否等于 10。如果是,则使用 break 关键字和值 counter * 2。循环结束后,使用分号结束将值赋给 result 的语句。最后,在 result 中打印值,在本例中为 20。

也可以从循环内部返回。break 只退出当前循环,而 return 总是退出当前函数。

循环标签消除多个循环之间的歧义

如果循环中有循环,则在该点中断并继续应用于最内层的循环。可以选择在循环上指定一个循环标签,然后可以使用 break 或 continue 来指定这些关键字应用于有标签的循环,而不是最内层的循环。循环标签必须以单引号开始。下面是两个嵌套循环的例子:

fn main() {let mut count = 0;'counting_up: loop {println!("count = {count}");let mut remaining = 10;loop {println!("remaining = {remaining}");if remaining == 9 {break;}if count == 2 {break 'counting_up;}remaining -= 1;}count += 1;}println!("End count = {count}");
}

外部循环具有 ‘counting_up’ 标签,它将从 0 数到 2。没有标签的内部循环从 10 数到 9。第一个没有指定标签的 break 只会退出内循环。

程序打印:

count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
带 while 的条件循环

示例:

fn main() {let mut number = 3;while number != 0 {println!("{number}!");number -= 1;}println!("LIFTOFF!!!");
}

这种结构消除了在使用 loop、if、else 和 break 时所必需的大量嵌套,并且更加清晰。当条件求值为 true 时,代码运行;否则,退出循环。

还可以使用 while 构造遍历集合(如数组)的元素。

示例:

fn main() {let a = [10, 20, 30, 40, 50];let mut index = 0;while index < 5 {println!("the value is: {}", a[index]);index += 1;}
}

这里,代码对数组中的元素进行计数。它从索引 0 开始,然后循环直到到达数组中的最终索引(也就是说,当 index < 5 不再为真时)。

然而,这种方法容易出错。如果索引值或测试条件不正确,我们可能会导致程序报错。例如,如果将 a 数组的定义更改为包含 4 个元素,但忘记将条件更新为 while index < 4,则代码将出现问题。它也很慢,因为编译器会添加运行时代码,在循环的每次迭代中执行索引是否在数组边界内的条件检查。

使用 for 循环遍历集合

作为一种更简洁的替代方法,可以使用 for 循环并为集合中的每个项执行一些代码。

示例:

fn main() {let a = [10, 20, 30, 40, 50];for element in a {println!("the value is: {element}");}
}

这样做提高了代码的安全性,并消除了可能由于超出数组的末尾或不够远而丢失某些项而导致的错误的可能性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80425.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c#加密证件号的中间部分,改为*号

前言 使用场景&#xff1a;在我项目中&#xff0c;我需要给前端提供接口&#xff0c;所以我要吧证件号进行加密。例如&#xff1a;411421199510225612&#xff0c;这是一个身份证号&#xff0c;18为的&#xff0c;那么我加密完成之后就会是 411421********5612&#xff0c;类似…

存储新势力:助力DeepSeek一体机

宝子们&#xff0c;今天要给大家分享一个超酷的科技话题——各大厂商陆续推出的DeepSeek训推一体机方案。 【集成人工智能训推平台】 它就像是一个超级智能的大脑中枢&#xff0c;为各种复杂的AI任务搭建AI模型流水线。预置算法模版、训练框架、推理框架、模型任务调度和自动…

同样机身尺寸下伺服电机比无刷电机扭矩更大的原因

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 88万阅读 1.6万收藏 在电机应用领域&#xff0c;伺服电机和无刷电机&#xff08;BLDC&#xff09;都是常见的动力源&#xff0c;但两者在性能上存在显著差异。尤其是在相同机身尺寸下&#xff0c;伺服电机的…

.dat 文件一般可以用什么打开

DAT文件是一种常见的文件格式,通常由多种应用程序生成。打开DAT文件的方法取决于其内容和生成它的软件。 使用文本编辑器 如果DAT文件是一个简单的文本文件,可以使用Windows的记事本或macOS的文本编辑器打开它。 右键点击文件 -> 选择“打开方式” -> 选择“记事本”…

Java实现加密(七)国密SM2算法的签名和验签(附商用密码检测相关国家标准/国密标准下载)

目录 一、国密标准中&#xff0c;关于SM2签名验签的定义二、SM2签名和验签的实现原理1. 前置知识2. 签名生成过程3. 验签过程4. 数学正确性证明5. 安全性与注意事项 三、带userId、不带userId的区别1. 核心区别2.算法区别(1) 哈希计算过程(2) 签名验签流程 四、Java代码实现1. …

Feign接口调用失败降级机制

是的&#xff0c;通过 FallbackFactory 实现的降级逻辑在 Feign 接口调用失败时会被触发&#xff0c;但需要注意以下关键点以确保降级生效&#xff1a; 一、代码有效性分析 降级逻辑是否生效&#xff1f; • 是的&#xff0c;当 Feign 调用 BaseServiceFeign 接口的 updateMoni…

React-JSX语法

1、React和Vue的区别 &#xff08;1&#xff09;设计理念&#xff1a;react是一个声明式UI库&#xff0c;强调的是函数式编程&#xff0c;学习难度较高&#xff0c;vue是渐进式框架&#xff0c;学习难度较低 &#xff08;2&#xff09;模板语法&#xff1a;react使用的是JSX语…

RocketMQ 主题与队列的协同作用解析(既然队列存储在不同的集群中,那要主题有什么用呢?)---管理命令、配置安装

学习之前呢需要会使用linux的基础命令 一.RocketMQ 主题与队列的协同作用解析 在 RocketMQ 中&#xff0c;‌主题&#xff08;Topic&#xff09;‌与‌队列&#xff08;Queue&#xff09;‌的协同设计实现了消息系统的逻辑抽象与物理存储分离。虽然队列实际存储在不同集群的 B…

三菱FX PLC频率采集

基于高速计数器&#xff0c;计算从X点输入方波个数&#xff0c;定时提取计数器值&#xff0c;换算得到频率。直接通过定时器数值判断来实现定时计数的精度不高&#xff0c;提高精度需要考虑定时中断方式。 初始化寄存器&#xff0c;通过M8235&#xff0c;M8236复位来选择C235&a…

一种专用车辆智能配电模块的设计解析:技术革新与未来展望

关键词&#xff1a;智能配电模块、STM32、CAN总线、电子开关、新能源汽车 引言&#xff1a;传统配电系统的痛点与智能化转型 传统配电系统依赖继电器和保险丝&#xff0c;存在体积大、寿命短、智能化低等缺陷&#xff08;如图1&#xff09;。而新能源汽车和无人驾驶技术对配电…

python——异常

1、定义 异常是在代码执行过程中发生的&#xff0c;它会影响到程序的正常运行。python程序不会自动来进行异常处理。python中常见异常父类&#xff1a;Exception。 2、常见异常 TypeError&#xff1a;类型错误异常。ValueError&#xff1a;值的异常。KeyError&#xff1a;键…

深入浅出Sentinel:分布式系统的流量防卫兵

引言 在当今的微服务架构和分布式系统中&#xff0c;服务间的依赖关系错综复杂&#xff0c;一个服务的故障可能会像多米诺骨牌一样引发整个系统的崩溃。如何有效地保护系统免受突发流量、不稳定依赖服务的影响&#xff0c;成为每个架构师和开发者必须面对的挑战。今天&#xf…

leetcode0106. 从中序与后序遍历序列构造二叉树-medium

1 题目&#xff1a;从中序与后序遍历序列构造二叉树 官方标定难度&#xff1a;中 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入…

【Pandas】pandas DataFrame rsub

Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象&#xff08;如 DataFrame、Series 或标量&#xff09;的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…

【信息系统项目管理师】高分论文:论人力资源管理与成本管理(医院信息系统)

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 论文一、规划人力资源管理二、组建项目团队三、建设项目团队四、管理项目团队论文 一个完善的医院信息系统通常由上百个子系统构成,而这些系统随着医院发展需求逐步建设的,他们来源于不同厂家,基于不同的技…

【python】如何将python程序封装为cpython的库

python程序在发布时&#xff0c;往往会打包为cpython的库&#xff0c;并且根据应用服务器的不同架构&#xff08;x86/aarch64&#xff09;&#xff0c;以及python的不同版本&#xff0c;封装的输出类型也是非常多。本文介绍不同架构指定python下的代码打包方式&#xff1a; 首…

Android 14 修改侧滑手势动画效果

涉及关键类 SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt 修改如下&#xff1a; 一&#xff0c;覆盖系统的默认手势效果 SystemUI/src/com/andro…

RHEL与CentOS:从同源到分流的开源操作系统演进

RHEL与CentOS&#xff1a;从同源到分流的开源操作系统演进 一、核心关系&#xff1a;源代码的重构与社区化 RHEL&#xff08;Red Hat Enterprise Linux&#xff09;与CentOS&#xff08;Community ENTerprise Operating System&#xff09;的关系可以概括为“同源异构”。RHE…

EFISH-SBC-RK3588 —— 厘米级定位 × 旗舰算力 × 工业级可靠‌

一、核心参数速览‌ ‌类别‌ ‌技术规格‌ ‌处理器‌ RK3588 八核&#xff08;4Cortex-A762.4GHz 4Cortex-A551.8GHz&#xff09; Mali-G610 GPU 6 TOPS NPU ‌定位能力‌ 双天线差分 GNSS&#xff08;GPS/北斗/GLONASS/Galileo&#xff09;&#xff0c;支持 RTK 动态…