函数定义
函数定义
函数参数
函数也可以被定义为拥有 参数(parameters),参数是特殊变量,是函数签名的一部分。当函数拥有参数(形参)时,可以为这些参数提供具体的值(实参)。技术上讲,这些具体值被称为参数(arguments),但是在日常交流中,人们倾向于不区分使用 parameter 和 argument 来表示函数定义中的变量或调用函数时传入的具体值。
参数声明
Rust 的函数参数声明和一般的变量声明相仿,也是参数名后加冒号,冒号后跟参数类型,不过不需要 let 关键字。需要注意的是,普通变量声明(let 语句)是可以省略变量类型的,而函数参数的声明则不能省略参数类型。来看一个简单例子:
fn main() {
say_hi("ruster");
}
fn say_hi(name: &str) {
println!("Hi, {}", name);
}
在函数签名中,必须 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解,意味着编译器不需要你在代码的其他地方注明类型来指出你的意图。当一个函数有多个参数时,使用逗号分隔,像这样:
fn main() {
another_function(5, 6);
}
fn another_function(x: i32, y: i32) {
println!("The value of x is: {}", x);
println!("The value of y is: {}", y);
}
将函数作为参数
在 Rust 中,函数是一等公民(可以储存在变量/数据结构中,可以作为参数传入函数,可以作为返回值),所以 rust 的函数参数不仅可以是一般的类型,也可以是函数。如:
fn main() {
let xm = "xiaoming";
let xh = "xiaohong";
say_what(xm, hi);
say_what(xh, hello);
}
fn hi(name: &str) {
println!("Hi, {}.", name);
}
fn hello(name: &str) {
println!("Hello, {}.", name);
}
fn say_what(name: &str, func: fn(&str)) {
func(name)
}
上例中,hi 函数和 hello 函数都是只有一个 &str 类型的参数且没有返回值。而 say_what 函数则有两个参数,一个是 &str 类型,另一个则是函数类型(function type),它是只有一个 &str 类型参数且没有返回值的函数类型。
函数返回值
函数可以向调用它的代码返回值。我们并不对返回值命名,但要在箭头(->)后声明它的类型。在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。使用 return 关键字和指定值,可从函数中提前返回;但大部分函数隐式的返回最后的表达式。这是一个有返回值的函数的例子:
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}
在 five
函数中没有函数调用、宏、甚至没有 let
语句——只有数字 5
。这在 Rust 中是一个完全有效的函数。注意,也指定了函数返回值的类型,就是 -> i32
。尝试运行代码;输出应该看起来像这样:
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
Running `target/debug/functions`
The value of x is: 5
five
函数的返回值是 5
,所以返回值类型是 i32
。让我们仔细检查一下这段代码。有两个重要的部分:首先,let x = five();
这一行表明我们使用函数的返回值初始化一个变量。因为 five
函数返回 5
,这一行与如下代码相同:
let x = 5;
其次,five
函数没有参数并定义了返回值类型,不过函数体只有单单一个 5
也没有分号,因为这是一个表达式,我们想要返回它的值。
Unit
在 Rust 中,任何函数都有返回类型,当函数返回时,会返回一个该类型的值。我们先来看看 main 函数:
fn main() {
//statements
}
之前有说过,函数的返回值类型是在参数列表后,加上箭头和类型来指定的。不过,一般我们看到的 main 函数的定义并没有这么做。这是因为 main 函数的返回值是(),在 rust 中,当一个函数返回 () 时,可以省略。main 函数的完整形式如下:
fn main() -> () {
//statements
}
main 函数的返回值类型是 (),它是一个特殊的元组,没有元素的元组,称为 unit,它表示一个函数没有任何信息需要返回。在 Rust Reference 的 Types 中是的描述如下:
For historical reasons and convenience, the tuple type with no elements (()) is often called ‘unit’ or ‘the unit type’.
() 类型,其实类似于 C/C++、Java、C#中的 void 类型。
返回值
下面来看一个有返回值的例子:
fn main() {
let a = 3;
println!("{}", inc(a));
}
fn inc(n: i32) -> i32 {
n + 1
}
上面的例子中,函数 inc
有一个 i32
类型的参数和返回值,作用是将参数加 1 返回。需要注意的是 inc
函数中只有 n+1
一个表达式,并没有像 C/C++或 Java、C#等语言有显式地 return
语句类返回一个值。这是因为,与其他基于语句的语言(如 C 语言)不同,rust 是基于表达式的语言,函数中最后一个表达式的值,默认作为返回值。
Rust 也有return
关键字,不过一般用于提前返回。来看一个简单地例子:
fn main() {
let a = [1,3,2,5,9,8];
println!("There is 7 in the array: {}", find(7, &a));
println!("There is 8 in the array: {}", find(8, &a));
}
fn find(n: i32, a: &[i32]) -> bool {
for i in a {
if *i == n {
return true;
}
}
false
}
上例中,find
函数,接受一个 i32
类型 n
和一个 i32
类型的切片(slice
) a
,返回一个bool
值,若 n 是 a 的元素,则返回 true
,否则返回 false
。可以看到,return
关键字,用在for
循环的if
表达式中,若此时 a 的元素与 n 相等,则立刻返回 true,剩下的循环不必再进行,否则一直循环检测完整个切片(slice),最后返回 false。当然,return 语句也可以用在最后返回,像 C/C++一样使用:把 find
函数最后一句 false
改为 return false;
(注意分号不可省略)也是可以的,不过这就不是 rust 的编程风格了。这里需要注意的是,for
循环中的 i
,其类型为 &i32
,需要使用解引用操作符来变换为 i32
类型。
返回多个值
rust 的函数不支持多返回值,但是我们可以利用元组来返回多个值,配合 rust 的模式匹配,使用起来十分灵活。先看例子:
fn main() {
let (p2,p3) = pow_2_3(789);
println!("pow 2 of 789 is {}.", p2);
println!("pow 3 of 789 is {}.", p3);
}
fn pow_2_3(n: i32) -> (i32, i32) {
(n*n, n*n*n)
}
可以看到,上例中,pow_2_3 函数接收一个 i32 类型的值,返回其二次方和三次方的值,这两个值包装在一个元组中返回。在 main 函数中,let 语句就可以使用模式匹配将函数返回的元组进行解构,将这两个返回值分别赋给 p2 和 p3,从而可以得到 789 二次方的值和三次方的值。
发散函数
发散函数(diverging function)是 rust 中的一个特性。发散函数不返回,它使用感叹号!
作为返回类型表示:
fn main() {
println!("hello");
diverging();
println!("world");
}
fn diverging() -> ! {
panic!("This function will never return");
}
由于发散函数不会返回,所以就算其后再有其他语句也是不会执行的。倘若其后还有其他语句,会出现编译警告。其中 panic!
是一个宏,使当前执行线程崩溃并打印给定信息。返回类型 !
可用作任何类型:
let x: i32 = diverges();
let y: String = diverges();