在 Rust '21 中捕获闭包

vik*_*784 12 closures reference move rust

我刚刚发现以下代码可以在 Rust 21 中编译(以前在 18 中不能编译)

fn get_func (i: &mut i32) -> impl Fn() -> i32 + '_ {
    || *i
} 
Run Code Online (Sandbox Code Playgroud)

是否有隐含的牵连i?如果是这样,那么为什么下面的代码也能编译?

fn get_func (i: &mut i32) -> impl Fn() -> i32 + '_ {
    let f = || *i;
    
    println!("{:?}", i);  // was expecting here to give borrow of moved variable error, `&mut` doesnt implement `Copy` trait
    
    f
}
Run Code Online (Sandbox Code Playgroud)

或者它是隐式移动(在本例中是复制)所指向的值?但是下面的代码应该可以编译,但没有编译——表明它正在移动引用。

fn get_func (i: &mut i32) -> impl Fn() -> i32 {
    || *i
}
Run Code Online (Sandbox Code Playgroud)

小智 2

它将您的等级降级&mut为 a&并根据需要制作尽可能多的副本。

&mut这都是拥有 的所有权限这一事实的副产品&;换句话说,可以从可变引用降级为不可变引用,并且可以根据需要多次复制非可变引用,因此这只是隐式执行这两项操作。

fn get_func (i: &mut i32) -> impl Fn() -> i32 + '_ {
    *i += 1; // fine, still mutable
    let f = || *i;
    println!("{}", *i); // fine, no mutability required
    *i += 1; // ERROR: cannot mutate *i while borrowed by f
    f
}
Run Code Online (Sandbox Code Playgroud)

值得注意的是,如果您确实尝试将其捕获为可变的,就会发生这种情况。

fn get_func (i: &mut i32) -> impl FnMut() -> i32 + '_ {
    println!("{}", *i); // Fine
    *i += 1; // Fine
    let f = || {*i += 1; *i};
    println!("{}", *i); // ERROR: cannot borrow *i as immutable
    f
}
Run Code Online (Sandbox Code Playgroud)

此外,生命周期不是指闭包的返回,而是指闭包本身。由于没有移动内存,因此只有当 的地址处的内存保持有效时,闭包才有效i。这就是为什么删除生命周期会使代码无法编译的原因。

无论如何,您真正需要记住的是经典:“一个可变引用或多个不可变引用,而不是两者”。