基础概念
- 使用
fn
关键字声明函数,函数参数与返回值类型需要手动写好fn add_two(num: i32) -> i32 { num + 2 }
- 当函数最后一句表达式没有写分号时,则将其作为函数结果返回,也可以显式写出
return
fn add_two(num: i32) -> i32 { num + 2 } fn add_two(num: i32) -> i32 { return num + 2; }
- 可以在函数当中声明函数,并能够在声明之前使用,但需要确保在有效的作用域范围当中,而非无限制
fn main() { { // OK my_fn(); fn my_fn(){ //... } // OK my_fn(); } // err my_fn(); }
- 不存在函数声明与函数表达式这一争论声音(相比 JS)
闭包
作为现代语言,Lambda(匿名函数)也是存在的,不过在 Rust 当中被称为 闭包(closure )。
// 简写形式
let add_one = |num| num + 1;
// 完整
let add_one = |num: i32| -> i32 {
num + 1
};
作为函数参数使用:
fn main() {
let num = 10;
let arr = [1, 2, 3, 4, 5];
let arr: Vec<i32> = arr.iter().map(|item| item * num).collect();
// [10, 20, 30, 40, 50]
println!("{:?}", arr);
}
这么一看,前端开发者就觉得怪了,这也能叫闭包?像是换了皮肤的箭头函数罢了,这需要做一个说明。
神奇的函数
虽然 Rust 与 JavaScript 一样,可以在函数当中声明函数,可该函数并不能读取函数作用域外的任何数据:
function main() {
let num = 10;
function log(){
console.log(num);
}
// OK
log();
}
fn main() {
let num = 10;
fn log() {
// 错误,不存在 num
println!("{}", num);
}
log();
}
想要读取外界数据时,需要将其切换为闭包形式:
fn main() {
let num = 10;
let log = || println!("{}", num);
// OK
log();
}
停顿一下,一函数不能读取当前作用域中的数据,其实就表明了,可以类似 JS 这么写,但是不能那么用,想使用数据,请显式传参。
要这么看,以后函数体内的函数声明,全使用闭包不就好了。并不行,虽然闭包可以读取外部数据,但不能自递归调用。
该论点可参考论坛中的讨论:
Is it possible to make a recursive closure in Rust?(stackoverflow)
小结
虽然可以在函数体内声明一个函数,但不可读取外界数据,不过这并不影响递归,如传统语言一般,将数据作为函数参数显示传入即可。
对此,想使用递归时,最好在函数体之外编写递归函数,因为写在执行代码当中的普通函数并没有什么优势,分离后还能条理清晰些。
而 JS 函数体内的函数,优势便在于减少不必要的传参
而闭包虽然可以读取数据,但是并不能自调用形成递归,更多时候是作为函数参数(Lambda 的主场)使用。
由此对比之下,像 JS 这种可随意拿取作用域当中任意数据的语言也是怪的离谱。