函数定义

函数定义

函数参数

函数也可以被定义为拥有 参数(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();
上一页