函数定义
和其他语言类似,要定义一个函数,我们需要指定它的名称、参数和返回值的类型:
fn plus_one(n: i32) -> i32 {
n + 1
}
如果函数不需要返回值,则不定义返回值类型。
return 关键字是可选的。以下情况可以省略 return :
- 使用了大括号而不是表达式体的函数,但最后一个表达式没有分号,且签名匹配了其类型,时;
- 函数体只有一行表达式时;
一等公民
Rust 中的函数是一等公民。
- 函数可以赋值给变量
let f = fn(x: i32) -> i32 { x + 1 };
fn main() {
let foo = f;
println!("{}", foo(1));
}
- 函数可以作为参数传递
fn apply(f: fn(i32) -> i32, x: i32) -> i32 {
f(x)
}
- 函数可以作为返回值
fn create_adder(y: i32) -> fn(i32) -> i32 {
fn add(x: i32) -> i32 {
x + y
}
add
}
-
高阶函数
高阶函数 (HOF) 是其参数和/或返回值本身就是函数的函数。换句话说,如果一种语言支持高阶函数,那么我们说这些函数是一等公民,也就是说它们是值。
fn map(f: fn(i32) -> i32, xs: Vec<i32>) -> Vec<i32> {
// ...
}
这意味着在 Rust 中,函数可以像其他类型一样进行传递、存储和操作。
函数作为一等公民,是 Rust 作为一门函数式编程语言的重要特性之一。它开启了许多函数式编程模式,如闭包、高阶函数、柯里化等。
匿名函数
有时没有必要命名函数。例如,我们可能希望动态定义一个函数,仅用一次。
fn main() {
println!("{}", binary_operator(5, 6, |a: i32, b: i32| a - b));
}
在上面的示例中,我们直接在对 的调用中定义了一个匿名函数。我们在管道之间定义参数列表,然后是函数本身的主体。
匿名函数是一个非常强大的工具,更重要的是,在 Rust 中,它们可以“捕获”封闭环境。在这种情况下,函数也称为闭包。
闭包的语法如下:
|参数| 返回值类型 {
// 函数体
}
- 无参数,无返回值:
let log = || {
println!("Logging!");
};
- 有参数,无返回值:
let add = |x, y| {
println!("{}", x + y);
};
- 有参数,有返回值:
let add = |x, y| -> i32 {
x + y
};
- 捕获环境变量:
let offset = 10;
let add = |x| x + offset;
匿名函数经常用于回调、迭代器中:
let v = vec![1, 2, 3];
v.iter().map(|x| x + 1); // 使用匿名函数
语句块表达式
前面提到
使用了大括号而不是表达式体的函数,但最后一个表达式没有分号,且签名匹配了其类型,时;
该如何理解呢?
在Rust中,语句块也可以是表达式的一部分。语句和表达式的区分方式是后面带不带分号(;)。如果带了分号,意味着这是一条语句, 它的类型是();如果不带分号,它的类型就是表达式的类型。示例如 下:
// 语句块可以是表达式,注意后面有分号结尾,x的类型是()
let x : () = { println!("Hello."); };
// Rust将按顺序执行语句块内的语句,并将最后一个表达式类型返回,y的类型是 i32
let y : i32 = { println!("Hello."); 5 };
//在函数中,我们也可以利用这样的特点来写返回值:
fn my_func() -> i32 {
// ... blablabla 各种语句
100
}
最后一条表达式没有加分号,因此整个语句块的类型就变成 了i32,刚好与函数的返回类型匹配。这种写法与return 100;语句的效 果是一样的,相较于return语句来说没有什么区别,但是更加简洁。
总结
Rust 中的函数特点:
-
使用 fn 关键字定义函数,参数和返回值声明使用括号包裹
-
函数体可以是一个表达式,也可以是代码块语句
-
返回值使用 -> 声明,可以省略返回关键字
-
支持多参数,多返回值,参数和返回值类型灵活
-
函数默认不可变,参数默认也是不可变
-
函数可以赋值、传参和返回,是一等公民
-
支持匿名函数(闭包)语法,方便回调和迭代
-
函数式编程范式友好