如何创建一个闭包来避免捕获变量的冗余克隆?

How do I make a closure that avoids a redundant clone of captured variables?

我正在尝试实现经典的 make_adder 函数,它接受一个可添加的东西和 returns 一个闭包,它接受另一个可添加的东西和 returns 总和。这是我的尝试:

use std::ops::Add;

fn make_adder<T: Add + Clone>(x: T) -> impl Fn(T) -> T::Output {
    move |y| x.clone() + y
}

因为我不想将 T 限制为 Copy,所以我在闭包内调用 clone()。我认为这也意味着总会有一个冗余的 x 被闭包捕获为 "prototype"。我能以某种方式做得更好吗?

实际上,您无法避免这一点。你永远不知道闭包是否会在下一次被调用;您将需要保留该值以防万一。在分析确定这是一个瓶颈之前,我不会担心执行克隆。


在某些情况下,您可以将闭包类型更改为 FnOnce,这会强制只能调用一次:

fn make_adder<T>(x: T) -> impl FnOnce(T) -> T::Output
where
    T: Add,
{
    move |y| x + y
}

在其他情况下,您也许可以为问题添加一些间接的方法。例如,不是传递 T,而是传递一个生成 T 的闭包(大概不是通过克隆它自己捕获的变量...)。这个T总是可以直接食用的:

fn make_adder<T>(x: impl Fn() -> T) -> impl Fn(T) -> T::Output
where
    T: Add,
{
    move |y| x() + y
}

如果您使用的类型支持引用加法(可能所有有用的类型都支持,包括内置数字类型),也许您可​​以使用引用。

fn make_adder<T, U>(x: T) -> impl Fn(T) -> U
where
    for<'a> &'a T: Add<T, Output = U>,
{
    move |y| &x + y
}

fn make_adder<'a, T>(x: &'a T) -> impl Fn(T) -> <&'a T as Add<T>>::Output
where
    &'a T: Add<T>,
{
    move |y| x + y
}