其实到现在为止,我们已经见过很多次 Rust
中的函数了。没错,就是之前在所有代码中呈现在眼前的 main
函数,该函数是大多数程序的入口处。细心的你一定也观察到了在 main
函数前面的关键字 fn
,猜的没错,这个关键字的作用就是用来声明一个函数的。
Rust
采用蛇形命名法(snake_case
) 作为函数和变量的常规样式,其中所有的字母都是小写,单词之间使用下划线 _
分隔。
示例:
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
在 Rust
中定义一个函数方法,在 fn
后跟函数名和一组花括号。花括号用来告诉编译器该函数体的开始和结束。
我们使用相应的函数名称后跟一组括号 ()
的方式来调用所定义的任何一个函数。可能细心的你已经观察到了, another_function
函数是定义在 main
函数之后的,但由于还在当前程序中,main
函数就可以直接调用。对于 Rust
而言不会关心自定义的函数定义的具体位置,只要是调用者所处的范围内,那么任何一个位置都是可以的。
现在来运行上述程序:
imaginemiracle:function$ cargo run
Compiling function v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/function)
Finished dev [unoptimized + debuginfo] target(s) in 0.63s
Running `target/debug/function`
Hello, world!
Another function.
可以看到程序按照预期一样,依照 main
函数语句的顺序,首先打印了 Hello, world!
之后调用 another_function
函数执行其内部的打印语句。
上面介绍了函数的声明方式,如上文所示,所定义的函数为无参函数。当然函数也可以为其声明参数(形参),调用者通过参数的方式向所定义的函数传入指定的值(实参),使该函数可以根据参数完成相对应的处理。
函数的参数是作为函数签名(Function's signature
)一部分的特殊变量。从技术上讲,被传入的具体值应称为 arguments
,但在平时的使用时,大多时候人们更多的使用 parameter
或 argument
两个词轮替来表述函数定义中的变量或调用函数时所传入的具体值。
示例:
声明带参函数:
fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {x}");
}
运行这个程序,查看结果:
imaginemiracle:function$ cargo run
Compiling function v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/function)
Finished dev [unoptimized + debuginfo] target(s) in 0.26s
Running `target/debug/function`
The value of x is: 5
函数 another_function
的声明中有一个名为 x
的参数,其类型被指定为 i32
。当调用该函数时,为其传入 5
这个具体的值,此时 prinln!
宏将 5
会放在由大括号括起来的 x
的位置。
在函数签名中,即在声明函数时,必须指定每个参数的具体类型。这一点是 Rust
设计中的一个深思熟虑的决定:“在函数定义中要求类型描述意味着编译器几乎不需要在代码的其它地方来确定开发者的意图。如果编译器知道函数期望的类型,它还能够提供更多有用的错误信息”。
示例:
声明含有多个参数的函数,定义多个参数时,需要使用逗号将其隔开:
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
类型的变量。函数体内部则会将这两个变量的具体值打印出来。
运行上述代码:
imaginemiracle:function$ cargo run
Compiling function v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/function)
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/function`
The measurement is: 5h
そのとおり
函数体是由一系列的语句和表达式组成,语句又或由以分号 ;
作为结尾的表达式组成。到目前为止所看到的函数都没有包含结束表达式,但我们已经看到了表达式作为语句的一部分。Rust
是一种基于表达式的语言,因此我们需要清楚的了解到表达式和语句的区别,这一点很重要。这一点是在其它语言中所不存在。
语句: 用来执行某些操作且不返回值的操作指令;
表达式: 用来计算并以计算的结果为返回值的操作。
示例:
fn main() {
let y = 6;
}
这是一个 main
函数的声明,该 main
函数的函数体中只包含了一条语句,即 let y = 6;
[注]:main 函数的声明也是语句,即 fn main() {...}整体可视为一条语句。
示例:
语句不会有返回值,因此,向下面这样直接使用一条 let
语句为另一个变量分配值的做法是错误的。
fn main() {
let x = (let y = 6);
}
当直接运行上面代码则会看到如下错误:
imaginemiracle:function$ cargo run
Compiling function v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/function)
error: expected expression, found statement (`let`)
--> src/main.rs:2:14
|
2 | let x = (let y = 6);
| ^^^^^^^^^
|
= note: variable declaration using `let` is a statement
error[E0658]: `let` expressions in this position are unstable
--> src/main.rs:2:14
|
2 | let x = (let y = 6);
| ^^^^^^^^^
|
= note: see issue #53667 for more information
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;
|
For more information about this error, try `rustc --explain E0658`.
warning: `function` (bin "function") generated 1 warning
error: could not compile `function` due to 2 previous errors; 1 warning emitted
由于 let y = 6
语句不会返回值,因此没有任何内容绑定到 x
上。这一点与其他编程语言不同,如在 C
和 Ruby
中,赋值语句会返回赋值的具体值,可以写成 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
,该语句计算的结果将会绑定到 y
上,成为整条变量定义语句的一部分。
仔细看, x + 1
这一行的末尾并没有分号,与其他大多数代码行不同。表达式不包括结束分号,如果在表达式的末尾添加分号,则会将其转换为语句,此时将不会返回值。
[注]:Rust 中语句不会返回任何值,切记这一点!
定义一个函数,该函数可以将处理的结果作为返回值返回给调用者。在声明函数时,不会为返回值命名,但必须在箭头 ->
后声明返回值的类型。在 Rust
中函数的返回值与函数体块中的最终表达式的值同义。
当然函数的返回值也可以通过使用 return
关键字指定一个值从函数中提前返回,但大多数函数会选择隐式返回值,即函数的最后一个表达式的结果,将会作为函数的返回值返回给调用者。
示例:
fn five() -> i32 {
5
}
fn main() {
let x = five(); // 等同于 let x = 5;
println!("The value of x is: {x}");
}
函数中没有任何的语句(函数调用、宏等),只有一个表达式 5
。这在 Rust
中是完全有效的函数。函数的返回值类型被指定为 i32
。
运行代码:
imaginemiracle:function$ cargo run
Compiling function v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/function)
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/function`
The value of x is: 5
再看下一个例子。
示例:
定义一个计算整数和的函数:
fn main() {
let x = 12;
let y = 28;
println!("{x} + {y} = {}", add(x, y));
}
fn add(x: i32, y: i32) -> i32 {
x + y
}
运行该代码:
imaginemiracle:function$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/function`
12 + 28 = 40
当我们在 x + y
表达式后添加一个分号,使其变为一条语句。
fn main() {
let x = 12;
let y = 28;
println!("{x} + {y} = {}", add(x, y));
}
fn add(x: i32, y: i32) -> i32 {
x + y;
}
再执行一下,我们将会得到如下错误。
imaginemiracle:function$ cargo run
Compiling function v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/function)
error[E0308]: mismatched types
--> src/main.rs:31:27
|
31 | fn add(x: i32, y: i32) -> i32 {
| --- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
32 | x + y;
| - help: consider removing this semicolon
For more information about this error, try `rustc --explain E0308`.
error: could not compile `function` due to previous error
主要错误消息“类型不匹配”揭示了此代码的核心问题。该函数的定义 add
表明它将返回一个 i32
,但语句不会计算出一个值,该值由单位类型 ()
表示。因此,没有返回任何内容,这与函数定义相矛盾并导致错误。在这个输出中,Rust
提供了一条简易性的信息,可能有助于纠正这个问题:它建议删除分号,这将修复错误。
开发人员为了使自己写的代码更容易被阅读代码的人所理解,虽然代码的逻辑本身足够清晰,但在代码量庞大或结构复杂的情况下分析起来还是会有些难度。这个时候就需要为代码加上额外的注释,帮助阅读者更加清晰的梳理逻辑。
在 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() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {y}");
}
*/
/*
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {x}");
}
*/
fn main() {
let x = 12;
let y = 28;
println!("{x} + {y} = {}", add(x, y));
}
fn add(x: i32, y: i32) -> i32 {
x + y
}
注释内容可以放在代码的末尾,用于描述当前行代码。
fn main() {
let lucky_number = 7; // I’m feeling lucky today
}
但是更经常地看到它们以这种格式使用,注释在它所注释的代码上方的单独一行中:
fn main() {
// I’m feeling lucky today
let lucky_number = 7;
}
Boys and Girls!!!
准备好了吗?下一节我们要开始做个小练习了哦!
不!我还没准备好,让我先回顾一下之前的。
上一篇《Rust语言——小小白的入门学习06》
我准备好了,掛かって来い(放马过来)!
下一篇《Rust语言——小小白的入门学习08》
觉得这篇文章对你有帮助的话,就留下一个赞吧v*
请尊重作者,转载还请注明出处!感谢配合~
[作者]: Imagine Miracle
[版权]: 本作品采用知识共享署名-非商业性-相同方式共享 4.0 国际许可协议进行许可。
[本文链接]: https://blog.csdn.net/qq_36393978/article/details/125717867