Как сделать замыкание, которое позволяет избежать избыточного клона захваченных переменных? - PullRequest
0 голосов
/ 15 января 2019

Я пытаюсь реализовать классическую функцию 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, захваченный замыканием как «прототип». Могу ли я как-нибудь сделать это лучше?

Ответы [ 2 ]

0 голосов
/ 16 января 2019

Возможно, вы можете использовать ссылку, если вы используете типы, которые поддерживают добавление к ссылкам (вероятно, все полезные, включая встроенные числовые типы).

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
}
0 голосов
/ 15 января 2019

Реально, вы не можете избежать этого. Вы никогда не знаете, будет ли закрытие вызвано в другой раз; вам нужно будет сохранить значение в случае, если оно есть. Я не буду беспокоиться о выполнении клона, пока профилирование не определит, что это узкое место.


В некоторых случаях вы можете изменить тип закрытия на 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
}
...