面试题答案
一键面试fn create_closure() -> impl Fn() {
let captured_variable = String::from("Hello, Rust!");
move || {
println!("{}", captured_variable);
}
}
闭包捕获环境变量的方式
- 按值捕获(
move
语义):使用move
关键字,闭包会获取变量的所有权。在上述例子中,captured_variable
被move
进闭包,外部作用域不再拥有该变量的所有权。这种方式适用于闭包可能会在外部变量作用域结束后仍然存活的情况。 - 按引用捕获:如果不使用
move
,闭包默认按引用捕获变量。闭包会借用外部变量,要求外部变量的生命周期至少和闭包一样长。例如:
fn create_closure_ref() -> impl Fn() {
let captured_variable = String::from("Hello, Rust!");
|| {
println!("{}", captured_variable);
}
}
这里闭包按引用捕获 captured_variable
,编译器会自动为闭包推断出合适的生命周期参数。
可能出现的生命周期问题
- 悬垂引用:当闭包按引用捕获变量,而该变量在闭包之前结束生命周期时,就会出现悬垂引用。例如:
fn create_bad_closure() -> impl Fn() {
let captured_variable;
{
let inner_variable = String::from("Hello, Rust!");
captured_variable = &inner_variable;
}
|| {
println!("{}", captured_variable); // 这里会报错,因为inner_variable已经离开作用域
}
}
- 生命周期不匹配:当闭包被返回,并且捕获了局部变量时,可能导致返回的闭包生命周期与局部变量生命周期不匹配。Rust 编译器会通过生命周期检查来确保这种情况不会发生。
解决办法
- 使用
move
关键字:如第一个例子所示,通过move
将变量所有权转移到闭包中,避免悬垂引用问题。 - 延长变量生命周期:确保捕获的变量生命周期足够长。例如,可以将变量定义在更外层作用域,或者使用
Box
等智能指针来控制变量的生命周期。例如:
fn create_good_closure() -> impl Fn() {
let captured_variable = Box::new(String::from("Hello, Rust!"));
move || {
println!("{}", captured_variable);
}
}
这里 Box
会在堆上分配内存,使得 captured_variable
的生命周期与闭包相匹配。