Подтвердить что ты не робот

Возвращение замыкания из функции

Примечание: этот вопрос был задан до первой стабильной версии Rust. С тех пор было много изменений, и синтаксис, используемый в функции, даже больше не действителен. Тем не менее, ответ Shepmaster превосходен и делает этот вопрос достойным внимания.


Наконец приземлились закрытые крышки, поэтому я экспериментирую с ними, чтобы увидеть, что вы можете сделать.

У меня есть эта простая функция:

fn make_adder(a: int, b: int) -> || -> int {
    || a + b
}

Тем не менее я получаю missing lifetime specifier [E0106] об ошибке missing lifetime specifier [E0106]. Я попытался исправить это, изменив тип возвращаемого значения на ||: 'static → int, но затем я получил еще одну ошибку, cannot infer an appropriate lifetime due to conflicting requirements

Если я правильно понимаю, закрытие распаковано, поэтому он владеет a и b. Мне кажется очень странным, что это нужно на всю жизнь. Как я могу это исправить?

4b9b3361

Ответ 1

Начиная с Rust 1.26, вы можете использовать impl trait:

fn make_adder(a: i32) -> impl Fn(i32) -> i32 {
    move |b| a + b
}

fn main() {
    println!("{}", make_adder(1)(2));
}

Это позволяет вернуть распакованное закрытие, даже если невозможно указать точный тип закрытия.

Это не поможет вам, если что-то из этого верно:

  1. Вы нацелены на Rust до этой версии

  2. У вас есть какие-то условия в вашей функции:

    fn make_adder(a: i32) -> impl Fn(i32) -> i32 {
        if a > 0 {
            move |b| a + b
        } else {
            move |b| a - b
        }
    }
    

    Здесь нет единственного возвращаемого типа; каждое закрытие имеет уникальный, не подлежащий определению тип.

  3. Вы должны быть в состоянии назвать возвращаемый тип по любой причине:

    struct Example<F>(F);
    
    fn make_it() -> Example<impl Fn()> {
        Example(|| println!("Hello"))
    }
    
    fn main() {
        let unnamed_type_ok = make_it();
        let named_type_bad: /* No valid type here */ = make_it();
    }
    

    Вы не можете (пока) использовать impl SomeTrait в качестве типа переменной.

В этих случаях вам нужно использовать косвенное обращение. Общее решение - это объект черты, как описано в другом ответе.

Ответ 2

Можно вернуть замыкания внутри Box es, т.е. как объекты объектов, реализующие определенный признак:

fn make_adder(a: i32) -> Box<Fn(i32) -> i32> {
    Box::new(move |b| a + b)
}

fn main() {
    println!("{}", make_adder(1)(2));
}

(попробуйте здесь)

Существует также RFC (его проблема отслеживания) при добавлении распакованных абстрактных типов возврата, которые позволяли бы возвращать закрытие по значению без полей, но этот RFC был отложен. Согласно обсуждению в RFC, кажется, что в последнее время в нем делается некоторая работа, поэтому вполне возможно, что типы нежелательных абстрактных возвратов будут доступны относительно скоро.

Ответ 3

Синтаксис || по-прежнему остается старым закрытым ящиком, поэтому это не работает по той же причине, что и раньше.

И он не будет работать даже с использованием правильного синтаксиса закрытия коробки |&:| -> int, так как это буквально просто сахар для определенных черт. В настоящий момент синтаксис сахара |X: args...| -> ret, где X может быть &, &mut или ничего, что соответствует Fn, FnMut, FnOnce вы можете также написать Fn<(args...), ret> и т.д. для несахарной формы. Сахар, вероятно, будет меняться (возможно, что-то вроде Fn(args...) -> ret).

Каждое незаблокированное закрытие имеет уникальный, невосприимчивый тип, сгенерированный внутри компилятором: единственный способ поговорить о распакованных закрываниях - через общие и ограниченные границы. В частности, запись

fn make_adder(a: int, b: int) -> |&:| -> int {
    |&:| a + b
}

похож на запись

fn make_adder(a: int, b: int) -> Fn<(), int> {
    |&:| a + b
}

то есть. говоря, что make_adder возвращает значение unboxed trait; что на данный момент не имеет особого смысла. Первое, что нужно попробовать:

fn make_adder<F: Fn<(), int>>(a: int, b: int) -> F {
    |&:| a + b
}

но это говорит о том, что make_adder возвращает любой F, который выбирает вызывающий, в то время как мы хотим сказать, что он возвращает некоторый фиксированный (но "скрытый" ) тип. Для этого требуются абстрактные типы возврата, в котором говорится, в основном, "возвращаемое значение реализует этот признак", все еще будучи разобранным и статически разрешенным. На языке этого (временно закрытого) RFC,

fn make_adder(a: int, b: int) -> impl Fn<(), int> {
    |&:| a + b
}

Или с закрытием сахара.

(Еще одна второстепенная точка: я не уверен на 100% в отношении незакрытых закрытий, но старые закрытия, безусловно, все еще захватывают вещи по ссылке, что является другой вещью, которая поглощает код, как предлагалось в этой проблеме. Это исправляется в # 16610.)