Два приведенных вами примера совершенно разные.
fn hof_five(a: i32, func: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 {
move |v| func(a + v)
}
Эта функция принимает закрытие типа, реализующего Fn(i32) -> i32
, и возвращает закрытие типа, реализующего эту черту, но аргумент и возврат типы разные типы. Тип аргумента определяется компилятором на основе передаваемого вами замыкания, а возвращаемый тип выводится на основе возвращаемого замыкания. Каждое замыкание имеет свой индивидуальный тип.
Поскольку тип аргумента и тип возвращаемого значения - это разные типы, ссылаться на них с одинаковыми именами невозможно. Единственное, что вы можете сделать, это определить черту, которая требует Fn(i32) -> i32
в качестве предварительного условия:
trait MyFn: Fn(i32) -> i32 {}
impl<T> MyFn for T
where
T: Fn(i32) -> i32,
{}
С этим определением черты вы можете переписать hof_five
как
fn hof_five(a: i32, func: impl MyFn) -> impl MyFn {
move |v| func(a + v)
}
общая реализация MyFn
гарантирует, что все замыкания, реализующие Fn(i32) -> i32
, автоматически также реализуют MyFn
.
fn hof_six(a: i32, func: Box<dyn Fn(i32) -> i32>) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |v| func(a + v))
}
Эта функция принимает указатель , указывающий на замыкание, реализующее Fn(i32) -> i32
, но конкретный тип этого замыкания не известен во время компиляции. Если вы на самом деле вызываете замыкание, конкретный тип замыкания определяется во время выполнения, и сгенерированный код динамически отправляется в нужную функцию. В этом случае тип аргумента и тип возвращаемого значения совпадают, поэтому вы можете использовать псевдоним типа, если хотите:
type MyBoxedFn = Box<dyn Fn(i32) -> i32>;
fn hof_six(a: i32, func: MyBoxedFn) -> MyBoxedFn {
Box::new(move |v| func(a + v))
}
Это полностью эквивалентно исходной версии.