Rust 函数
本笔记主要来自于 Rust 程序设计语言 中文版 [3.3],旨在记录个人学习过程中的重点和心得体会。在记录过程中,笔者会对文档中的某些内容进行摘抄或修改,并添加自己的注释或说明。如有不当之处,请指正。
函数在 Rust 代码中很普遍,我们已经见过语言中最重要的函数之一:main
函数,他是很多程序的入口点。同时也见过 fn
关键字,它是用来声明新函数。
Rust 代码中的函数和变量名使用下划线命名法(snake case 蛇形命名法)规范风格。在下划线命名法中,所有字母都是小写并使用下划线分隔单词。
fn main() {
println!("Hello, world!");
}
fn fetch_list_function() {
println!("fetch list function");
}
Rust 中使用 fn
来定义一个函数,后面跟着函数名和一对小括号 ()
,花括号 {}
告诉编译器函数体在哪开始在哪结束({}
也是一个代码块作用域的开始和结束)。
可以使用函数名后面跟着小括号来调用我们定义过的函数。我们可以尝试在 main
函数中调用他们。在 Rust 中,函数的定义没有先后之分,你可以定义在 main
函数的前面和后面,Rust 不管定函数定义在哪,反正只要定义了就可以。
fn main() {
println!("Hello, world!");
fetch_list_function();
}
fn fetch_list_function() {
println!("fetch list function");
}
# clover @ MacBook-Pro in ~/dev/rust/learn/function on git:master x [10:06:10]
$ cargo run
Compiling function v0.1.0 (/Users/clover/dev/rust/learn/function)
Finished dev [unoptimized + debuginfo] target(s) in 0.98s
Running `target/debug/function`
Hello, world!
fetch_list_function
Rust 中的代是自上而下执行,他默认是同步执行的。在这个 main
方法中,它先打印 Hello, world!
再调用 fetch_list_function
方法然后打印 fetch list function
参数
函数也可以被定义为拥有参数(parameter),参数是特殊变量,也是函数签名的一部分。当函数拥有参数时(形参),可以为这些参数提供具体的值(实参)。人们倾向不区分使用 parameter 和 argument 来表示函数中定义的参数或调用函数时传入的具体值。
fn main() {
fetch_list_with_age(20)
}
fn fetch_list_with_age(age: i8) {
println!("argument.age ====>>> {}", age)
}
# clover @ MacBook-Pro in ~/dev/rust/learn/function on git:master x [10:14:01]
$ cargo run
Compiling function v0.1.0 (/Users/clover/dev/rust/learn/function)
Finished dev [unoptimized + debuginfo] target(s) in 0.51s
Running `target/debug/function`
argument.age ====>>> 20
fetch_list_with_age
函数的声明中有一个命名为 age
的参数,age
的类型被指定为 i8
。在 main
函数中调用 fetch_list_with_age
函数的时候传入了 20。在 fetch_list_with_age
函数执行时打印这个 age
参数,此时 age
是 20。
在函数声明中,如果存在参数,那么必须声明每个参数的类型。这是 Rust 设计中经过慎重考虑的决定:要求在函数定义提供类型标注,以为着编译器几乎从不需要你在代码的其它地方注明类型来指出你的意图。
当函数有多个参数时使用逗号分隔:
fn main() {
fetch_list_with_age_name_sex(20, "clover", true);
}
/// @param age 年龄
/// @param name 姓名
/// @param sex 性别
fn fetch_list_with_age_name_sex(age: u8, name: &str, sex: bool) {
println!("age {} name {} sex {}", age, name, sex);
}
这个方法声明了3个参数,分别是 u8
类型的 age
参数、&str
类型的 name
参数和一个 bool
类型的 sex
参数。
# clover @ MacBook-Pro in ~/dev/rust/learn/function on git:master x [10:31:16]
$ cargo run
Compiling function v0.1.0 (/Users/clover/dev/rust/learn/function)
Finished dev [unoptimized + debuginfo] target(s) in 0.33s
Running `target/debug/function`
age 20 name clover sex true
语句和表达式
函数由一系列语句组成,也可以以表达式结尾,因为 Rust 是一门基于表达式(expression-based)的语言,这是一个需要理解的重要区别,其它语言没有这个区别。
语句是执行一些操作但不返回值的指令。而表达式(expression)计算并产生一个值。
在之前的笔记中,通过一个 let
变量绑定一个值,这是表达式的一种。函数定义也是语句,下面例子本身就是一个语句。
fn main() {
let y = 1;
}
语句不返回值,因此无法将一个 let
语句赋值给另外一个变量,如果我们这么做了,会产生一系列错误:
let y = (let x = 2);
# clover @ MacBook-Pro in ~/dev/rust/learn/function on git:master x [10:51:19] C:101
$ cargo run
Compiling function v0.1.0 (/Users/clover/dev/rust/learn/function)
error: expected expression, found `let` statement
--> src/main.rs:2:14
|
2 | let y = (let x = 2);
| ^^^
error: expected expression, found statement (`let`)
--> src/main.rs:2:14
|
2 | let y = (let x = 2);
| ^^^^^^^^^
|
= note: variable declaration using `let` is a statement
error[E0658]: `let` expressions in this position are unstable
--> src/main.rs:2:14
|
2 | let y = (let x = 2);
| ^^^^^^^^^
|
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
warning: unnecessary parentheses around assigned value
--> src/main.rs:2:13
|
2 | let y = (let x = 2);
| ^ ^
|
= note: `#[warn(unused_parens)]` on by default
help: remove these parentheses
|
2 - let y = (let x = 2);
2 + let y = let x = 2;
|
For more information about this error, try `rustc --explain E0658`.
warning: `function` (bin "function") generated 1 warning
error: could not compile `function` due to 3 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 = 6;
5 + x
};
println!("y = {}", y);
}
例如这个表达式:
{
let x = 6;
5 + x
}
它是一个代码块,在这个例子中,计算结果是11。这个值作为 let
语句的一部分被搬定到 y
上。需要注意的是 5 + x
没有分号,这和之前见过的大部分代码都不同。表达式的结尾没有分号,如果在表达式的末尾加上分号,那它就被转成语句而语句不会有返回值。
如果我们函数有返回值并且以表达式结尾,那么可以忽略 return 语句直接写表达式
带有返回值的函数
函数可以向调用它的代码返回一个值,我们不需要对返回值命名,但是要在 ->
后面声明它的类型。在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。使用 return
关键字和指定值,可以提前返回。但大部分函数隐式返回最后一个表达式。下面这段代码就是一个具有返回值的函数。
fn main() {
let x = five();
println!("x {}", x)
}
fn five() -> i32 {
5
}
在 five
函数中没有函数调用、宏,设置没有 let
语句,只有一个数字5本身。这在 Rust 中是一个完全有效的函数,因为它是一个表达式。这个函数的返回值被指定好了,即: -> i32
。
# clover @ MacBook-Pro in ~/dev/rust/learn/function on git:master x [11:32:01] C:101
$ cargo run
Compiling function v0.1.0 (/Users/clover/dev/rust/learn/function)
Finished dev [unoptimized + debuginfo] target(s) in 0.61s
Running `target/debug/function`
x 5
如果我运行下面这个代码
fn main() {
let x = six();
println!("x {}", x)
}
fn six() -> i32 {
5 + 1;
}
运行后抛出了一个错误
# clover @ MacBook-Pro in ~/dev/rust/learn/function on git:master x [11:44:12] C:101
$ cargo run
Compiling function v0.1.0 (/Users/clover/dev/rust/learn/function)
error[E0308]: mismatched types
--> src/main.rs:35:14
|
35 | fn five() -> i32 {
| ---- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
36 | 5 + 1;
| - help: remove this semicolon to return this value
For more information about this error, try `rustc --explain E0308`.
error: could not compile `function` due to previous error
意思是这个 five
函数需要一个 i32
类型的返回值,但得到了一个 ()
。这是因为加了 ;
后,我们的表达式被转成了语句,语句是没有返回值的,而这个方法需要一个返回值。
在这个错误输出中,Rust 提供了一条信息,它可能有助于修正这个错误:它建议删除分号,这将修复这个错误。