功能

函数在 Rust 代码中很普遍。您已经见过最 语言中的重要函数:函数,即入口 许多程序的点。您还看到了关键字,它允许您 声明新函数。mainfn

Rust 代码使用蛇形大小写作为函数和变量的常规样式 names,其中所有字母均为小写,并为单独的单词添加下划线。 下面是一个包含示例函数定义的程序:

文件名: src/main.rs

fn main() {
    println!("Hello, world!");

    another_function();
}

fn another_function() {
    println!("Another function.");
}

我们在 Rust 中定义一个函数,方法是输入,后跟一个函数名称和一个 一组括号。大括号告诉编译器函数 身体开始和结束。fn

我们可以通过输入其名称后跟集合来调用我们定义的任何函数 的括号。因为 在程序中定义,所以它可以是 从函数内部调用。请注意,我们在源代码中的 after the function 之后定义了;我们之前可以定义它 也。Rust 不在乎你在哪里定义你的函数,只关心它们在哪里 在调用方可以看到的作用域中的某个位置定义。another_functionmainanother_functionmain

让我们启动一个名为 functions 的新二进制项目来探索函数 进一步。将示例放在 src/main.rs 中并运行它。你 应看到以下输出:another_function

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

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

参数

我们可以定义函数来具有参数,这些参数是特殊的变量 是函数签名的一部分。当函数具有参数时,您可以 为其提供这些参数的具体值。从技术上讲,混凝土 值称为参数,但在随意的对话中,人们倾向于使用 words 参数参数可互换用于任一变量 在函数的定义中,或者调用时传入的具体值 功能。

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

文件名: src/main.rs

fn main() {
    another_function(5);
}

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

尝试运行此程序;您应该得到以下输出:

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

的声明具有一个名为 的参数。的类型指定为 。当我们传入 时,宏将包含的一对大括号放在 is 的位置 在 format string.another_functionxxi325another_functionprintln!5x

在函数签名中,必须声明每个参数的类型。这是 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}");
}

此示例创建一个名为 2 的函数 参数。第一个参数已命名,是一个 .第二个是 named 和 is 类型 。然后,该函数打印包含 和 .print_labeled_measurementvaluei32unit_labelcharvalueunit_label

让我们尝试运行这段代码。将函数项目的 src/main.rs 文件中的当前程序替换为前面的示例,并使用以下命令运行它:cargo run

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

因为我们调用了函数 as 的值 for 和 as 的值 ,则程序输出包含这些值。5value'h'unit_label

语句和表达式

函数体由一系列语句组成,可选地以 表达。到目前为止,我们介绍的函数尚未包含结尾 expression,但您已将 expression 视为语句的一部分。因为 Rust 是一种基于表达式的语言,这是 理解。其他语言没有相同的区别,所以让我们看看 什么是语句和表达式以及它们的差异如何影响身体 的功能。

  • 语句是执行某些作但不返回的指令 一个值。
  • 表达式的计算结果为结果值。让我们看一些例子。

我们实际上已经使用了 statements 和 expressions。创建变量并 使用关键字为其赋值是一个语句。在示例 3-1 中,是一个 statement。letlet 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 run
   Compiling 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` expressions

warning: 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

该语句不返回值,因此没有要绑定到的任何内容。这与其他语言中发生的情况不同,例如 C 和 Ruby,其中赋值返回赋值。在那些 languages 中,您可以编写 and have both 和 have the value ;在 Rust 中不是这种情况。let y = 6xx = y = 6xy6

表达式的计算结果为一个值,并构成了其余代码的大部分 您将使用 Rust 编写。考虑一个数学运算,例如 ,它是一个 表达式,该表达式的计算结果为值 .表达式可以是 statements:在示例 3-1 中,语句中的 in 是一个 表达式,该表达式的计算结果为值 .调用函数是一个 表达。调用宏是一个表达式。使用 大括号是一个表达式,例如:5 + 6116let y = 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
}

是一个块,在本例中,其计算结果为 。该值作为语句的一部分绑定到。请注意,该行没有 分号,这与你目前看到的大多数行不同。 表达式不包括结束分号。如果在末尾添加分号 表达式中,您可以将其转换为语句,然后它不会返回 价值。在探索函数返回值和表达式时,请记住这一点 下一个。4yletx + 1

具有返回值的函数

函数可以将值返回给调用它们的代码。我们不命名 return 值,但我们必须在箭头 () 后声明它们的类型。在 Rust 中, 函数的返回值与最终的 expression 在函数体的块中。您可以从 函数,但大多数 函数隐式返回最后一个表达式。下面是一个 函数返回一个值:->return

文件名: src/main.rs

fn five() -> i32 {
    5
}

fn main() {
    let x = five();

    println!("The value of x is: {x}");
}

函数中没有函数调用、宏,甚至没有语句,只有数字本身。这是一个完全有效的函数 锈。请注意,该函数的返回类型也被指定为 .尝试 运行此代码;输出应如下所示:letfive5-> i32

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

in 是函数的返回值,这就是返回类型 是。让我们更详细地研究一下。有两个重要的部分: 首先,该行显示我们使用的是 函数来初始化变量。由于该函数返回一个 , 该行与以下内容相同:5fivei32let x = five();five5

#![allow(unused)]
fn main() {
let x = 5;
}

其次,该函数没有参数,并定义了 返回值,但函数的主体是没有分号的 因为它是我们要返回其值的表达式。five5

让我们看另一个例子:

文件名: 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
}

运行此代码将打印 .但是,如果我们放置一个 分号,将其从 expression 添加到语句中,我们将收到一个错误:The value of x is: 6x + 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 run
   Compiling 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 value

For more information about this error, try `rustc --explain E0308`.
error: could not compile `functions` (bin "functions") due to 1 previous error

主要错误消息 揭示了此问题的核心问题 法典。该函数的定义表示它将返回一个 ,但语句不会计算出一个值,该值由 表示 , 单位类型。因此,不会返回任何内容,这与函数 定义并导致错误。在此输出中,Rust 向 可能有助于纠正这个问题:它建议删除分号,它 将修复错误。mismatched typesplus_onei32()

本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准