面试题答案
一键面试闭包捕获变量的方式
- 按值捕获(
Copy
类型):- 捕获方式:当闭包捕获的变量实现了
Copy
trait时,闭包会按值捕获变量。这意味着闭包获取变量的一份拷贝,而原始变量仍然可以在闭包外部继续使用。 - 所有权和生命周期:闭包对捕获的变量有独立的所有权,其生命周期与闭包本身的生命周期相同。当闭包超出作用域时,捕获的变量也会被释放(如果有需要释放的资源)。
- 示例:
- 捕获方式:当闭包捕获的变量实现了
fn main() {
let num = 5;
let closure = || println!("The number is: {}", num);
closure();
println!("num is still available: {}", num);
}
在这个例子中,num
是i32
类型,实现了Copy
trait。闭包closure
按值捕获num
,并且num
在闭包调用后仍然可以在外部使用。
- 按值捕获(非
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();
}
这里,s
是String
类型,没有实现Copy
trait。闭包closure
通过move
关键字获取String
的所有权,所以println!
试图使用 s
时会导致编译错误。
- 按引用捕获:
- 捕获方式:闭包可以按引用捕获变量,这样闭包不会获取变量的所有权,原始变量仍然可以在闭包外部使用。
- 所有权和生命周期:闭包持有对变量的引用,引用的生命周期受Rust的借用规则限制。闭包的生命周期不能超过它所引用变量的生命周期。
- 示例:
fn main() {
let mut num = 5;
let closure = || num += 1;
closure();
println!("num after closure: {}", num);
}
在这个例子中,闭包按引用捕获num
,因为闭包并没有获取num
的所有权,所以可以在闭包外部修改和使用num
。
闭包在迭代器方法中的应用场景及原理
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
方法根据这个条件过滤出偶数。
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
中。