MST

星途 面试题库

面试题:Rust闭包基础与应用

在Rust中,闭包是如何捕获其环境中的变量的?请举例说明闭包捕获变量的三种方式,并解释每种方式在所有权和生命周期方面的特点。同时,阐述闭包在迭代器方法(如`filter`、`map`)中的常见应用场景及原理。
37.2万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

闭包捕获变量的方式

  1. 按值捕获(Copy类型)
    • 捕获方式:当闭包捕获的变量实现了Copy trait时,闭包会按值捕获变量。这意味着闭包获取变量的一份拷贝,而原始变量仍然可以在闭包外部继续使用。
    • 所有权和生命周期:闭包对捕获的变量有独立的所有权,其生命周期与闭包本身的生命周期相同。当闭包超出作用域时,捕获的变量也会被释放(如果有需要释放的资源)。
    • 示例
fn main() {
    let num = 5;
    let closure = || println!("The number is: {}", num);
    closure();
    println!("num is still available: {}", num);
}

在这个例子中,numi32类型,实现了Copy trait。闭包closure按值捕获num,并且num在闭包调用后仍然可以在外部使用。

  1. 按值捕获(非Copy类型)
    • 捕获方式:对于没有实现Copy trait的类型,闭包会获取变量的所有权。这意味着原始变量在闭包创建后不能在闭包外部使用。
    • 所有权和生命周期:闭包获得变量的所有权,变量的生命周期与闭包的生命周期绑定。当闭包超出作用域时,变量所占用的资源会被释放。
    • 示例
fn main() {
    let s = String::from("hello");
    let closure = move || println!("The string is: {}", s);
    // println!("s is not available here: {}", s); // 这行会编译错误
    closure();
}

这里,sString类型,没有实现Copy trait。闭包closure通过move关键字获取String的所有权,所以println!试图使用 s时会导致编译错误。

  1. 按引用捕获
    • 捕获方式:闭包可以按引用捕获变量,这样闭包不会获取变量的所有权,原始变量仍然可以在闭包外部使用。
    • 所有权和生命周期:闭包持有对变量的引用,引用的生命周期受Rust的借用规则限制。闭包的生命周期不能超过它所引用变量的生命周期。
    • 示例
fn main() {
    let mut num = 5;
    let closure = || num += 1;
    closure();
    println!("num after closure: {}", num);
}

在这个例子中,闭包按引用捕获num,因为闭包并没有获取num的所有权,所以可以在闭包外部修改和使用num

闭包在迭代器方法中的应用场景及原理

  1. filter方法
    • 应用场景filter方法用于根据特定条件过滤迭代器中的元素。闭包作为filter的参数,定义了过滤条件。只有满足闭包条件的元素才会被保留在结果迭代器中。
    • 原理filter方法遍历迭代器中的每个元素,并将每个元素传递给闭包。闭包返回一个bool值,true表示保留该元素,false表示丢弃该元素。最终,filter方法返回一个新的迭代器,其中只包含满足条件的元素。
    • 示例
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let even_numbers: Vec<i32> = numbers.iter().filter(|&num| num % 2 == 0).cloned().collect();
    println!("Even numbers: {:?}", even_numbers);
}

这里闭包|&num| num % 2 == 0定义了过滤偶数的条件,filter方法根据这个条件过滤出偶数。

  1. map方法
    • 应用场景map方法用于对迭代器中的每个元素应用一个转换函数。闭包作为map的参数,定义了转换逻辑,将每个元素转换为新的元素。
    • 原理map方法遍历迭代器中的每个元素,并将每个元素传递给闭包。闭包对元素进行转换并返回新的元素,map方法返回一个新的迭代器,其中包含转换后的元素。
    • 示例
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let squared_numbers: Vec<i32> = numbers.iter().map(|num| num * num).collect();
    println!("Squared numbers: {:?}", squared_numbers);
}

闭包|num| num * num将每个元素平方,map方法将这些平方后的结果收集到一个新的Vec中。