了解 Fn / FnOnce 闭包

Understanding Fn / FnOnce closures

在下面的例子中,我不明白为什么第一个例子允许闭包是Fn,而第二个例子只允许FnOnce.

示例 1:

fn with_client(
    client: BasicClient,
) -> impl Filter<Extract = (BasicClient,), Error = Infallible> + Clone {
    warp::any().map(move || client.clone())
}

示例 2:

fn with_client(
    client: BasicClient,
) -> impl Filter<Extract = (BasicClient,), Error = Infallible> + Clone {
    let clone = client.clone();
    warp::any().map(move || clone)
}

如果我尝试 运行 第二个示例,我会收到一个编译器警告,提示闭包是 FnOnce,因为它需要将 clone 移动到闭包中。情况确实如此,但我看不出这与第一个示例有何不同,我们需要将 client 移动到闭包中?

让我们手动对闭包进行脱糖处理。

struct Closure1 {
    client: BasicClient,
}
impl FnOnce<()> for Closure1 {
    type Output = BasicClient;
    extern "rust-call" fn call_once(self, (): ()) -> BasicClient {
        <Self as Fn<()>>::call(&self, ())
    }
}
impl FnMut<()> for Closure1 {
    extern "rust-call" fn call_mut(&mut self, (): ()) -> BasicClient {
        <Self as Fn<()>>::call(&*self, ())
    }
}
impl Fn<()> for Closure1 {
    extern "rust-call" fn call(&self, (): ()) -> BasicClient {
        <BasicClient as Clone>::clone(&self.client)
    }
}

struct Closure2 {
    client: BasicClient,
}
impl FnOnce<()> for Closure2 {
    type Output = BasicClient;
    extern "rust-call" fn call_once(self, (): ()) -> BasicClient {
        self.client
    }
}

fn with_client(
    client: BasicClient,
) -> impl Filter<Extract = (BasicClient,), Error = Infallible> + Clone {
    warp::any().map(Closure1 { client })
}

fn with_client(
    client: BasicClient,
) -> impl Filter<Extract = (BasicClient,), Error = Infallible> + Clone {
    let clone = <BaseClient as Clone>::clone(&clone);
    warp::any().map(Closure1 { client: clone })
}

如您所见,问题不在于我们将 client 移动到 闭包中。问题是我们将它 移出 闭包。当你搬出某物时,你必须拥有数据的所有权:因此,FnOnce。您正在使用克隆创建闭包这一事实并不重要。但是,当您在闭包 中克隆 时,您仅使用共享引用 - 因此您不需要所有权,甚至不需要独占访问权 - 因此 Fn.