Как использовать функции Fn/закрытия в подписях в Rust

Я хочу написать функцию int -returning, которая принимает замыкание, принимающее нулевые аргументы, замыкание, принимающее один аргумент, и замыкание с двумя аргументами, где все аргументы закрытия имеют тип int, и каждое замыкание возвращает f32.

Какова будет подпись этой функции?

Теперь я хочу принять через признаки Fn и FnMut. Как выглядит подпись? Требуется ли использование элементов в ящике? Если да, то какие и почему?

Если известно: что это похоже на сахарирование? Обессахаренный?

Если известно: что может измениться в будущем?

Ответ 1

Я хочу написать функцию int-return, которая принимает замыкание взяв нулевые аргументы, замыкание, принимающее один аргумент, и замыкание принимая два аргумента, где все аргументы замыкания имеют тип int и каждое замыкание возвращает f32.

Какова будет подпись этой функции?

Подпись функции и ее использование в настоящее время (2014-10-26 в ночное время) могут выглядеть так:

#![feature(unboxed_closures, unboxed_closure_sugar, overloaded_calls)]

fn closures<F1, F2, F3>(mut f1: F1, mut f2: F2, mut f3: F3) -> int
    where F1: FnMut() -> f32,
          F2: FnMut(int) -> f32,
          F3: FnMut(int, int) -> f32 {
    (f1() + f2(10) + f3(20, 30)) as int
}

fn main() {
    let x = closures(
        |&mut:| 0.1,
        |&mut: x: int| (2*x) as f32,
        |&mut: x: int, y: int| (x + y) as f32
    );
    println!("{}", x);
}

Вы можете использовать Fn вместо FnMut (и удалять mut до f1, f2 и f3), если вы хотите заставить вызывающего пользователя пройти замыкания, которые не изменяют их окружение, но в целом, я думаю, вы бы хотели использовать FnMut.

Этот код использует распакованный сахар и перегруженные вызовы. Без них это будет выглядеть так:

#![feature(unboxed_closures)]

fn closures<F1, F2, F3>(mut f1: F1, mut f2: F2, mut f3: F3) -> int
    where F1: FnMut<(), f32>,
          F2: FnMut<(int,), f32>,
          F3: FnMut<(int, int), f32> {
    (f1.call_mut(()) + f2.call_mut((10,)) + f3.call_mut((20, 30))) as int
}

fn main() {
    let x = closures(
        |&mut:| 0.1,
        |&mut: x: int| (2*x) as f32,
        |&mut: x: int, y: int| (x + y) as f32
    );
    println!("{}", x);
}

Сахар используется для префикса синтаксиса типа закрытия, а функция перегруженных вызовов позволяет опустить явные методы call_*.

Что касается того, что будет изменяться в будущем, то, скорее всего, упрощение синтаксиса закрытия будет упрощено (когда текущие закрытия будут удалены), поэтому бит main() будет выглядеть следующим образом:

fn main() {
    let x = closures(
        || 0.1,
        |x| (2*x) as f32,
        |x, y| (x + y) as f32
    );
    println!("{}", x);
}

Фактический тип закрытия (FnMut, Fn или FnOnce) будет выведен.

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

В общем случае закрытые блокировки описаны в this RFC. Однако он не обновляется с новым синтаксисом сахара и другими незначительными изменениями; может быть лучше следовать Rust трекер вопроса, чтобы узнать об этом подробнее. Например, многие проблемы с распакованными замыканиями агрегируются в этой ошибке.

Ответ 2

Fn, FnMut и FnOnce - это три типа признаков, которые были введены с незакрытыми замыканиями. Разница между этими признаками, помимо имени их единственного метода, заключается в том, что параметр self для этих методов передается по-разному:

  • Fn: &self (по ссылке не может мутировать среду закрытия)
  • FnMut: &mut self (по ссылке, может мутировать среду закрытия)
  • FnOnce: self (по значению, занимает закрытие, поэтому замыкание можно вызывать только один раз)

Эти черты имеют два типа параметров: Args, который является типом набора, который представляет параметры закрытия (или (), если закрытие не принимает параметров) и Result, что является типом возврата закрытия.

Теперь, чтобы ответить на ваш вопрос:

  • Закрытие с использованием нулевых аргументов:

    fn foo<F: Fn<(), f32>>(closure: F) -> int {
        0
    }
    
    • В качестве альтернативы, граница может быть записана с помощью предложения where:

      fn foo<F>(closure: F) -> int where F: Fn<(), f32> {
          0
      }
      
  • Закрытие одного аргумента:

    fn foo<F: Fn<(int), f32>>(closure: F) -> int {
        0
    }
    
  • Закрытие с двумя аргументами:

    fn foo<F: Fn<(int, int), f32>>(closure: F) -> int {
        0
    }
    
  • Закрытие с использованием нулевых аргументов, sugared form:

    fn foo<F: Fn() -> f32>(closure: F) -> int {
        0
    }
    
  • Закрытие одного аргумента, сахарозная форма:

    fn foo<F: Fn(int) -> f32>(closure: F) -> int {
        0
    }
    
  • Закрытие с двумя аргументами, sugared form:

    fn foo<F: Fn(int, int) -> f32>(closure: F) -> int {
        0
    }
    

Старые закрытые "коробочные" затворы уходят. Вы можете отслеживать ошибки при распакованных закрываниях в metug.