Rust 函数

article/2025/6/6 22:54:17

文章目录

  • Rust 函数
    • 函数
    • 参数
    • 语句与表达式
    • 带返回值的函数
    • 代码示例

Rust 函数

Rust 函数

函数

函数在 Rust 代码中非常常见。你已经见过了语言中最重要的函数之一:main 函数,它是许多程序的入口点。你还见过 fn 关键字,它允许你声明新的函数。

Rust 代码采用蛇形命名法(snake case)作为函数和变量名的惯用风格,即所有字母小写,单词之间用下划线分隔。下面是一个包含函数定义示例的程序:

文件名:src/main.rs

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

我们通过输入 fn 后跟函数名和一对括号来定义 Rust 中的函数。花括号告诉编译器函数体的开始和结束位置。

我们可以通过输入函数名和一对括号来调用任何已定义的函数。因为 another_function 已在程序中定义,所以可以在 main 函数内部调用它。注意,我们在源代码中将 another_function 定义在 main 函数之后;也可以在之前定义。Rust 不关心你在哪里定义函数,只要它们在调用者可见的作用域中定义即可。

让我们新建一个名为 functions 的二进制项目,进一步探索函数。将 another_function 示例放入 src/main.rs 并运行。你应该会看到如下输出:

$ cargo runCompiling functions v0.1.0 (file:///projects/functions)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.28sRunning `target/debug/functions`
Hello, world!
Another function.

这些行按照它们在 main 函数中出现的顺序执行。首先打印 “Hello, world!” 消息,然后调用 another_function 并打印其消息。

参数

我们可以定义带有参数的函数,参数是函数签名的一部分的特殊变量。当函数有参数时,你可以为这些参数提供具体的值。严格来说,这些具体的值称为实参(arguments),但在日常交流中,人们往往将参数(parameter)和实参(argument)混用,既指函数定义中的变量,也指调用函数时传入的具体值。

在这个版本的 another_function 中,我们添加了一个参数:

文件名:src/main.rs

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

尝试运行这个程序,你应该会得到如下输出:

$ cargo runCompiling functions v0.1.0 (file:///projects/functions)Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.21sRunning `target/debug/functions`
The value of x is: 5

another_function 的声明有一个名为 x 的参数。x 的类型被指定为 i32。当我们将 5 传递给 another_function 时,println! 宏会将 5 放到格式字符串中包含 x 的花括号位置。

在函数签名中,必须声明每个参数的类型。这是 Rust 设计中的一个有意决定:要求在函数定义中进行类型注解,意味着编译器几乎不需要你在代码的其他地方使用类型注解来推断类型。如果编译器知道函数期望的类型,也能给出更有帮助的错误信息。

定义多个参数时,用逗号分隔参数声明,如下所示:

文件名:src/main.rs

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

这个例子创建了一个名为 print_labeled_measurement 的函数,带有两个参数。第一个参数名为 value,类型为 i32。第二个参数名为 unit_label,类型为 char。然后该函数打印包含 valueunit_label 的文本。

让我们尝试运行这段代码。用上面的例子替换 functions 项目 src/main.rs 文件中的程序,并使用 cargo run 运行:

$ cargo runCompiling functions v0.1.0 (file:///projects/functions)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31sRunning `target/debug/functions`
The measurement is: 5h

因为我们调用函数时,value 的值为 5,unit_label 的值为 ‘h’,所以程序输出包含了这些值。

语句与表达式

函数体由一系列语句组成,最后可以以一个表达式结尾。到目前为止,我们介绍的函数还没有包含结尾的表达式,但你已经见过语句中的表达式。由于 Rust 是一门基于表达式的语言,这一区别很重要。其他语言没有这样的区分,所以让我们看看语句和表达式是什么,以及它们的区别如何影响函数体。

语句是执行某些操作但不返回值的指令。
表达式会计算并产生一个值。让我们看一些例子。
实际上,我们已经用过语句和表达式。用 let 关键字创建变量并赋值是一个语句。在示例 3-1 中,let y = 6; 是一个语句。

文件名:src/main.rs

fn main() {let y = 6;
}

示例 3-1:包含一个语句的 main 函数声明

函数定义也是语句;整个前面的例子本身就是一个语句。(如下面所示,调用函数不是语句。)

语句不返回值。因此,你不能将 let 语句赋值给另一个变量,如下面的代码尝试做的那样;你会得到一个错误:

文件名:src/main.rs

此代码无法编译!

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

运行这个程序时,你会得到如下错误:

$ cargo runCompiling functions v0.1.0 (file:///projects/functions)
error: expected expression, found `let` statement--> src/main.rs:2:14|
2 |     let x = (let y = 6);|              ^^^|= note: only supported directly in conditions of `if` and `while` expressionswarning: unnecessary parentheses around assigned value--> src/main.rs:2:13|
2 |     let x = (let y = 6);|             ^         ^|= note: `#[warn(unused_parens)]` on by default
help: remove these parentheses|
2 -     let x = (let y = 6);
2 +     let x = let y = 6;|warning: `functions` (bin "functions") generated 1 warning
error: could not compile `functions` (bin "functions") due to 1 previous error; 1 warning emitted

let y = 6 语句不返回值,所以没有任何东西可以绑定给 x。这与其他语言(如 C 和 Ruby)不同,在那些语言中,赋值会返回赋值的值。你可以写 x = y = 6,让 xy 都等于 6;但在 Rust 中不是这样。

表达式会计算出一个值,并构成你在 Rust 中编写的大部分代码。比如数学运算 5 + 6 是一个表达式,计算结果为 11。表达式可以作为语句的一部分:在示例 3-1 中,语句 let y = 6; 中的 6 就是一个表达式,计算结果为 6。调用函数是表达式。调用宏是表达式。用花括号创建的新作用域块也是表达式,例如:

文件名:src/main.rs

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关键字及指定值提前返回,但大多数函数会隐式返回最后一个表达式。下面是一个返回值的函数示例:

文件名:src/main.rs

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

five 函数中没有函数调用、宏调用,甚至没有 let 语句——只有一个单独的数字 5。这在 Rust 中是完全合法的函数。注意函数的返回类型也被指定为 -> i32。试着运行这段代码,输出应该如下:

$ cargo runCompiling functions v0.1.0 (file:///projects/functions)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30sRunning `target/debug/functions`
The value of x is: 5

five 函数中的 5 就是函数的返回值,因此返回类型为 i32。让我们更详细地分析一下。有两个重要点:首先,let x = five(); 这一行表明我们用函数的返回值初始化变量。因为 five 返回 5,这一行等价于:

let x = 5;

其次,five 函数没有参数,并定义了返回值的类型,但函数体只有一个没有分号的 5,因为它是我们想要返回值的表达式。

让我们再看一个例子:

文件名:src/main.rs

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

运行这段代码会打印 The value of x is: 6。但如果我们在 x + 1 这一行末尾加上分号,把它从表达式变成语句,就会报错:

文件名:src/main.rs

此代码无法编译!

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

编译这段代码会产生如下错误:

$ cargo runCompiling functions v0.1.0 (file:///projects/functions)
error[E0308]: mismatched types--> src/main.rs:7:24|
7 | fn plus_one(x: i32) -> i32 {|    --------            ^^^ expected `i32`, found `()`|    ||    implicitly returns `()` as its body has no tail or `return` expression
8 |     x + 1;|          - help: remove this semicolon to return this valueFor more information about this error, try `rustc --explain E0308`.
error: could not compile `functions` (bin "functions") due to 1 previous error

主要的错误信息 mismatched types 揭示了代码的核心问题。plus_one 函数的定义声明它会返回 i32,但语句不会计算出值,表达为 (),即单元类型。因此没有返回任何值,这与函数定义矛盾,导致报错。在输出中,Rust 提供了可能的修复建议:移除分号,这样就能修复错误。

代码示例

fn main() {println!("Hello, world!");// another_function();another_function(5);print_labeled_measurement(5, 'h');println!("The value of function give_me_five is: {}", give_me_five());let x = plus_one(5);println!("The value of x is: {x}");
}// fn another_function() {
//     println!("Another function.");
// }fn another_function(x: i32) {println!("The value of x is: {x}");
}fn print_labeled_measurement(value: i32, unit_label: char) {println!("The measurement is: {value}{unit_label}");
}fn give_me_five() -> i32 {5
}fn plus_one(x: i32) -> i32 {x + 1
}

运行结果:

Hello, world!
The value of x is: 5
The measurement is: 5h
The value of function give_me_five is: 5
The value of x is: 6

http://www.hkcw.cn/article/SPstiTsYRq.shtml

相关文章

【Typst】3.Typst脚本语法

概述 Typst的核心就是它在标记语法的基础上提供了一个灵活强大的脚本语言,来支持复杂的排版操作。 本节就以一个脚本语言的角度,介绍一下Typst脚本的核心语法。 系列目录 1.Typst概述2.Typst标记语法和基础样式3.Typst脚本语法4.导入、包含和读取5.文…

Java 文件操作 和 IO(5)-- 综合案例练习 -- 示例三

文章目录 题目描述:扫描指定目录,并找到文件名称或文件内容中包含指定字符的所有普通文件(不包含目录)结果案例演示:设计思路:总体的思路:使用代码,分步实现1. 准备工作(…

微深节能 筒仓卸料小车远程智能控制系统 格雷母线

微深节能筒仓卸料小车远程智能控制系统——格雷母线高精度定位解决方案 在现代化筒仓物料管理中,卸料小车的精准定位与远程控制是提升效率、保障安全的关键。武汉市微深节能科技有限公司推出的格雷母线高精度位移测量系统,为筒仓卸料小车提供远程智能控…

股票指数期货的变动与股票价格指数的关系是什么?

很多小伙伴刚接触金融投资的时候,常常会听到“股票指数期货”和“股票价格指数”这两个词,但搞不清楚它们之间的关系。今天,我就给大家讲讲,这两个东西到底是什么关系。 一、股票价格指数是个什么? 股票价格指数&…

2025LitCTF re wp复现

LitCTF2025 wp&&复现 easy_rc4 魔改RC4,直接在异或处下条件断点,动调获取密钥流 FeatureExtraction 定位到main 前面都是一些初始化函数以及把输入的char型字符串转成int型数据 关键加密在sub_401722(Block, des) 加密逻辑就是 unsigned in…

Lovable + Cursor:零基础搭建专业应用的秘密武器

🚀 Lovable + Cursor:零基础搭建专业应用的秘密武器 为什么你需要这个工作流? 想象一下这样的场景:你用Lovable快速搭建了一个漂亮的网页原型,但当你想要添加更复杂的功能时,却发现自己被限制住了。或者你在Cursor里写代码很顺手,但每次从零开始设计界面都让你头疼不…

ARM GIC V3概述

中断类型 locality- specific peripheral interrupt(LPI):LPI是一个有针对性的外设中断,通过affinity路由到特定的PE。 为非安全group1中断边沿触发可以通过its进行路由没有active状态,所以不需要明确的停用操作LPI总…

Docker部署与应用、指令

部署 【Docker】在 Ubuntu 22.04 以下版本上安装 Docker 的详细指南_ubuntu 安装docker-CSDN博客 应用 使用指定镜像创建并运行一个新容器 --name,指定容器名称 -d 代表后台运行 nginx 代表容器镜像名 docker ps 查看运行的容器 -a 查看…

内网横向之RDP缓存利用

RDP(远程桌面协议)在连接过程中会缓存凭据,尤其是在启用了 "保存密码" 或 "凭据管理器" 功能时。这个缓存的凭据通常是用于自动填充和简化后续连接的过程。凭据一般包含了用户的用户名和密码信息,或者是经过加…

从计量到通信,DJSF1352-D为快充桩系统提供了怎样的解决方案?

摘要 随着新能源汽车保有量的不断攀升,直流充电桩成为城市交通与能源基础设施的重要组成部分。电能计量作为充电桩运营、结算和安全管理的核心环节,对计量设备提出了更高的要求。安科瑞DJSF1352D导轨式直流电能表,凭借高精度、高稳定性和通信…

0518蚂蚁暑期实习上机考试题1:数组操作

题目 小红认为一个长度为 n 的数组 a 是好的,当且仅当对于任意的 i ,均满足相等,其中数组下标 i 从 1 开始,小红每次可以对一个数加 1 或者减 1 ,求把给定的数组变成好数组的最少操作次数。 输入描述:第一…

深入对比主流Java Web服务器与框架

目录 一、核心技术对比概览 二、深度解析与应用场景 1. Apache Tomcat - 企业级标准容器 2. Netty - 高性能网络编程框架 3. Undertow - 轻量级嵌入式服务器 4. Vert.x - 响应式应用框架 5. Play Framework - 全栈Web框架 三、性能基准测试对比(参考数据&am…

晶台光耦在手机PD快充上的应用

光耦(光电隔离器)作为关键电子元件,在手机PD快充中扮演信号隔离与传输的“安全卫士”。其通过光信号实现电气隔离,保护手机电路免受高电压损害,同时支持实时信号反馈,优化充电效率。 晶台品牌推出KL817、KL…

EscapeX:去中心化游戏,开启极限娱乐新体验

VEX 平台推出全新去中心化游戏 EscapeX(数字逃脫),创新性地将大逃杀玩法与区块链技术相融合。用户不仅能畅享紧张刺激的解谜过程,更能在去中心化、公正透明的环境中参与游戏。EscapeX 的上线,为 VEX 生态注入全新活力&…

服务端定时器的学习(一)

一、定时器 1、定时器是什么? 定时器不仅存在于硬件领域,在软件层面(客户端、网页和服务端)也普遍应用,核心功能都是高效管理大量延时任务。不同应用场景下,其实现方式和使用方法有所差异。 2、定时器解…

Axure形状类组件图标库(共8套)

点击下载《月下倚楼图标库(形状组件)》 原型效果:https://axhub.im/ax9/02043f78e1b4386f/#g1 摘要 本图标库集锦精心汇集了8套专为Axure设计的形状类图标资源,旨在为产品经理、UI/UX设计师以及开发人员提供丰富多样的设计素材,提升原型设计…

CET6 仔细阅读 24年12月第一套-C1 大脑这一块

文章 There are hundreds of personality quizzes online that assert they can ascertain whether the right or left half of your brain is dominant. Left-brained people are supposedly logical and excel at language and math while right- brained people are more i…

【JavaWeb】SpringBoot原理

1 配置优先级 在前面,已经学习了SpringBoot项目当中支持的三类配置文件: application.properties application.yml application.yaml 在SpringBoot项目当中,我们要想配置一个属性,通过这三种方式当中的任意一种来配置都可以&a…

硬件工程师笔记——555定时器应用Multisim电路仿真实验汇总

目录 一 555定时器基础知识 二、引脚功能 三、工作模式 1. 单稳态模式: 2. 双稳态模式(需要外部电路辅助): 3. 无稳态模式(多谐振荡器): 4. 可控脉冲宽度调制(PWM&#xff09…

基于springboot的图书管理系统的设计与实现

其他源码获取可以看首页:代码老y 个人简介:专注于毕业设计项目定制开发:springbootvue系统,Java微信小程序,javaSSM系统等技术开发,并提供远程调试部署、代码讲解、文档指导、ppt制作等技术指导。源码获取&…