【问题标题】:How do I make a closure that avoids a redundant clone of captured variables?如何创建一个避免捕获变量的冗余克隆的闭包?
【发布时间】:2019-01-15 00:07:29
【问题描述】:

我正在尝试实现经典的make_adder 函数,该函数接受一个可添加的事物并返回一个闭包,该闭包接受另一个可添加的事物并返回总和。这是我的尝试:

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 被闭包捕获为“原型”。我能以某种方式做得更好吗?

【问题讨论】:

    标签: rust closures clone


    【解决方案1】:

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


    在某些情况下,您可以将闭包类型更改为 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
    }
    

    【讨论】:

      【解决方案2】:

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

      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
      }
      

      【讨论】:

      • 第一个并没有真正解决问题,AFAICT。闭包仍然拥有一个值的副本,除了闭包本身之外,其他任何东西都不会使用它。
      • @Shepmaster 它避免了闭包内的clone(),这是OP的关注点之一。我同意第二个可能会更好,如果您愿意让调用者通过参考。
      • 我的意思是,您可以通过创建另一个名称不同的特征来“避免 clone”,但正如我看到的 OP 问题,根本问题是“浪费”的对象。
      猜你喜欢
      • 1970-01-01
      • 2019-01-02
      • 2011-06-16
      • 1970-01-01
      • 1970-01-01
      • 2016-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多