Zizheng Tai
Zizheng Tai

Reputation: 6646

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

I'm trying to implement the classic make_adder function which takes an addable thing and returns a closure which takes another addable thing and returns the sum. Here is my attempt:

use std::ops::Add;

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

Because I don't want to restrict T to be Copy, I'm calling clone() inside the closure. I think this also means there will always be one redundant x captured by the closure as the "prototype". Can I somehow do this better?

Upvotes: 2

Views: 174

Answers (2)

Anders Kaseorg
Anders Kaseorg

Reputation: 3875

Perhaps you can use a reference, if you’re using types that support addition on references (probably all the useful ones do, including the built-in numeric types).

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

or

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
}

Upvotes: 2

Shepmaster
Shepmaster

Reputation: 432089

Realistically, you cannot avoid this. You never know if the closure will be called another time; you will need to keep the value in case it is. I wouldn't worry about performing the clone until profiling has identified that this is a bottleneck.


In certain cases, you might be able to change your closure type to FnOnce, which enforces that it can only be called exactly once:

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

In other cases, you might be able to add some indirection to the problem. For example, instead of passing a T, pass in a closure that generates a T (presumably not by cloning its own captured variable...). This T can always be consumed directly:

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

Upvotes: 3

Related Questions