了解 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
.
在下面的例子中,我不明白为什么第一个例子允许闭包是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
.