前端都能看懂的rust入门教程(二)——函数和闭包

19 阅读3分钟

Rust 中的函数(Functions)

函数使用 fn 关键字定义,语法和typescript类似。

基本语法:

fn function_name(parameter: Type) -> ReturnType {
    // 函数体
    expression  // 如果没有分号,这是返回值
}
  • 参数:指定类型,如 x: i32。参数默认不可变,除非用 mut 标记。
  • 返回类型:用 -> 指定。如果无返回值,默认为 ()(单元类型)。
  • 返回值:函数体最后一行表达式(无分号)作为返回值,或用 return 显式返回

示例:一个简单的加法函数

fn add(a: i32, b: i32) -> i32 {
    a + b  // 无分号,返回结果
}

fn main() {
    let result = add(5, 3);
    println!("Result: {}", result);  // 输出: Result: 8
}

Rust函数的特点:

  • 所有权转移这是最重要的特点。如果参数是拥有类型(如 String),函数会获得所有权,除非用引用(如 &String)。所有权介绍
  • 泛型函数:支持泛型。
  • 高阶函数:函数可以作为参数或返回值,例如接受闭包的函数。

Rust 中的闭包(Closures)

闭包是匿名函数,可以捕获其定义环境中的变量。Rust 的闭包类似于js中的箭头函数。

基本语法:

闭包的基本语法为|参数| 表达式 或 |参数| {语句块}。

let closure_name = |parameters| -> ReturnType { body };
  • 参数在 之间。
  • 类型通常可推断,但可显式指定。
  • 闭包可以是表达式形式(单行)或块形式(多行)。

示例:简单闭包

// 最简单的闭包:没有参数,直接返回一个值
let c = || 42;
println!("{}", c()); // 42

// 带参数的闭包,参数类型可省略(由编译器推断)
let add_one = |x| x + 1;
println!("{}", add_one(5)); // 6

// 多参数闭包
let sum = |a, b| a + b;
println!("{}", sum(3, 4)); // 7

// 使用花括号编写多行语句
let complicated = |x: i32| {
    let y = x * 2;
    y + 10
};
println!("{}", complicated(5)); // 20

捕获环境:

闭包可以“捕获”外部变量,根据捕获方式分为三种 trait(trait可以简单理解为Typescript中的类型,但更关注行为抽象。):

  • Fn:不可变借用环境(&)。适用于只读访问。

    let x = 42;
    let print_x = || println!("x: {}", x);  // 捕获 &x
    print_x();
    
  • FnMut:可变借用环境(&mut)。允许修改捕获变量。

    let mut x = 42;
    let mut increment_x = || x += 1;  // 捕获 &mut x
    increment_x();
    println!("x: {}", x);  // 输出: x: 43
    
  • FnOnce:移动所有权(消耗环境)。闭包只能调用一次。

    let x = vec![1, 2, 3];
    let consume_x = move || drop(x);  // 使用 move 强制移动所有权
    consume_x();  // 之后 x 不可用
    
  • 特点

    • move 关键字:强制闭包获取变量的所有权,即使不需要修改。
    • trait 界限:闭包实现 Fn、FnMut 或 FnOnce trait。
    • 与函数的区别:闭包可以捕获上下文,而函数不能。

闭包的trait

闭包的trait分为Fn/FnMut/FnOnce,但在写闭包的时候都是通过字面量形式,闭包的trait由编译器自动推导,所以无需关注。但当我们定义函数的参数为闭包时,就需要定义它的trait。

fn call_once<F>(f: F)
where
    F: FnOnce(),
{
    f(); // 只能调用一次
}

fn call_mut<F>(mut f: F)
where
    F: FnMut(),
{
    f(); // 可以多次调用
    f();
}

比如

use std::thread;

fn main() { 
    let mut sum = 0; 
    let numbers = vec![1, 2, 3, 4]; 
    numbers.iter().for_each(|&x| sum += x); // 闭包捕获 &mut sum,实现 FnMut 
    println!("Sum: {}", sum); // 输出: Sum: 10
    
    let data = vec![1, 2, 3]; 
    let handle = thread::spawn(move || { // move 强制实现 FnOnce 
        println!("Data in thread: {:?}", data); // 消耗 data 
    }); 
    handle.join().unwrap(); 
}

其中 for_each 的签名是 fn for_each(self, f: F) where F: FnMut(Self::Item);,spawn 的签名是 fn spawn(f: F) -> JoinHandle where F: FnOnce() -> T + Send + 'static;