указатель функции против объекта типажа Fn

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { // definition
    f(arg) + f(arg)
}

do_twice(|x| x + 1, 5) // call

Эта функция принимает как замыкания, так и указатели на функции. Он принимает указатель на функцию в качестве типа параметра.

Когда я должен предпочесть это использованию трейт-объекта, например &dyn Fn(i32) -> i32 или Box<dyn Fn(i32)-> i32> вместо fn

fn do_twice(f: &dyn Fn(i32) -> i32, arg: i32) -> i32 { // definition
    f(arg) + f(arg)
}

do_twice(&|x| x + 1, 5) // call

or

fn do_twice(f: Box<dyn Fn(i32) -> i32>, arg: i32) -> i32 { // definition
    f(arg) + f(arg)
}

person Akshay Mariyanna    schedule 14.01.2019    source источник
comment
Спасибо, что прояснили мой запрос. Параметры типа fn могут принимать только замыкания, которые не захватывают среду. Поэтому следует использовать типичный объект, когда такое ограничение не предназначено.   -  person Akshay Mariyanna    schedule 14.01.2019
comment
Для справки (в дополнение к ответам): RFC 1558 предлагает автоматически принуждать замыкания, которые не фиксируют свое окружение, к указателям fn. Благодаря этому RFC ваш пример работает (возможно, неожиданно).   -  person Lukas Kalbertodt    schedule 14.01.2019


Ответы (2)


fn — это указатель на голую функцию (https://doc.rust-lang.org/std/primitive.fn.html).

Это не будет работать с закрытием, которое захватывает среду, и его нельзя реализовать вручную для вашего причудливого типа (например, impl Fn for MySuperType)

Таким образом, единственная причина, по которой ваши примеры работают, заключается в том, что они слишком упрощены!

если вы сделаете это немного сложнее, это не удастся https://gist.github.com/rust-play/2167e73325daa1e2a179505209405917

person Darth Kotik    schedule 14.01.2019

Когда я должен предпочесть это использованию трейт-объекта

Черта-объекты — не единственный вариант. Как указал @DarthKotik, принятие указателя fn не позволит замыканиям, которые захватывают их среду, но вы можете просто использовать параметр нормального типа, ограниченный Fn, чтобы принимать как функции, так и замыкания, без необходимости что-либо упаковывать:

fn do_twice<F>(f: F, arg: i32) -> i32 
where
    F: Fn(i32) -> i32
{
    f(arg) + f(arg)
}

Или, что то же самое, но без дополнительной переменной типа:

fn do_twice(f: impl Fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}
person Peter Hall    schedule 14.01.2019