Есть ли более короткий способ написания сигнатур функций, которые принимают и возвращают замыкания? - PullRequest
1 голос
/ 08 апреля 2020

Есть ли более короткий способ записи этих сигнатур функций?

fn hof_five(a: i32, func: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 {
    move |v| func(a + v)
}

fn hof_six(a: i32, func: Box<dyn Fn(i32) -> i32>) -> Box<dyn Fn(i32) -> i32> {
    Box::new(move |v| func(a + v))
}

Что-то вроде использования F: impl Fn(i32) -> i32 or F: Box<dyn Fn(i32) -> i32>

Так что подпись становится примерно такой:

fn hof_five<F>(a: i32, func: F) -> F {
    move |v| func(a + v)
}

Ответы [ 2 ]

2 голосов
/ 08 апреля 2020

Два приведенных вами примера совершенно разные.

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))
}

Это полностью эквивалентно исходной версии.

1 голос
/ 08 апреля 2020

Бесполезно, нет, нет.

Чтобы сделать что-то короче, обычно нужно уменьшить избыточность или удалить символы.

  • Нет semanti c избыточность, поскольку аргумент и тип возвращаемого значения означают разные вещи.
  • impl и fn и i32 уже сокращены, поэтому они не могут удалять символы.

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

macro_rules! F {
    () => (impl Fn(i32) -> i32);
}

fn hof_five(a: i32, func: F!()) -> F!() {
    move |v| func(a + v)
}

См. Также:


Возможно, вас также заинтересует использование where пункты для разложения сложности:

fn hof_five<F>(a: i32, func: F) -> impl Fn(i32) -> i32
where
    F: Fn(i32) -> i32,
{
    move |v| func(a + v)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...