Возврат функции
Возврат фактического указателя функции требует выделения кучи и обертки:
use std::future::Future;
use std::pin::Pin;
pub async fn some_async_func(arg: &str) {}
pub fn some_async_func_wrapper<'a>(arg: &'a str)
-> Pin<Box<dyn Future<Output=()> + 'a>>
{
Box::pin(some_async_func(arg))
}
pub fn higher_order_func<'a>(action: &str)
-> fn(&'a str) -> Pin<Box<dyn Future<Output=()> + 'a>>
{
some_async_func_wrapper
}
Почему бокс? higher_order_func
должен иметь конкретный тип возврата, который является указателем на функцию. Указанная функция также должна иметь конкретный тип возврата, что невозможно для функции async
, поскольку она возвращает непрозрачный тип. Теоретически, можно было бы написать тип возвращаемого значения как fn(&'a str) -> impl Future<Output=()> + 'a
, но это потребовало бы гораздо больше догадок от компилятора и в настоящее время не поддерживается.
Если вы в порядке с Fn
вместо fn
, вы можете избавиться от оболочки:
pub async fn some_async_func(arg: &str) {}
pub fn higher_order_func<'a>(action: &str)
-> impl Fn(&'a str) -> Pin<Box<dyn Future<Output=()> + 'a>>
{
|arg: &'a str| {
Box::pin(some_async_func(arg))
}
}
Чтобы вернуть другую функцию, основанную на значении action
, вам нужно будет поместить само замыкание в коробку, что является еще одним выделением кучи:
pub async fn some_async_func_one(arg: &str) {}
pub async fn some_async_func_two(arg: &str) {}
pub fn higher_order_func<'a>(action: &str)
-> Box<dyn Fn(&'a str) -> Pin<Box<dyn Future<Output=()> + 'a>>>
{
if action.starts_with("one") {
Box::new(|arg: &'a str| {
Box::pin(some_async_func_one(arg))
})
} else {
Box::new(|arg: &'a str| {
Box::pin(some_async_func_two(arg))
})
}
}
Альтернатива: возвращение будущего
Чтобы упростить вещи, рассмотрите возможность возврата самого будущего вместо указателя функции. Это практически то же самое, но гораздо приятнее и не требует выделения кучи:
pub async fn some_async_func(arg: &str) {}
pub fn higher_order_func_future<'a>(action: &str, arg: &'a str)
-> impl Future<Output=()> + 'a
{
some_async_func(arg)
}
Может выглядеть, когда вызывается higher_order_func_future
, выполняется some_async_func
- но это не дело. Из-за того, что работают функции asyn c, при вызове some_async_func
, пользовательский код не выполняется . Вызов функции возвращает Future
: фактическое тело функции будет выполнено только , когда кто-то ожидает возвращенного будущего.
Вы можете использовать новую функцию почти так же, как и предыдущую функцию. :
// With higher order function returning function pointer
async fn my_function() {
let action = "one";
let arg = "hello";
higher_order_func(action)(arg).await;
}
// With higher order function returning future
async fn my_function() {
let action = "one";
let arg = "hello";
higher_order_func_future(action, arg).await;
}
Еще раз обратите внимание, что в обоих случаях фактическое some_async_func
тело выполняется только , когда ожидается будущее.
Если вы хотели чтобы иметь возможность вызывать различные асин c функции, основанные на значении action
, вам нужно снова создать бокс:
pub async fn some_async_func_one(arg: &str) {}
pub async fn some_async_func_two(arg: &str) {}
pub fn higher_order_func_future<'a>(action: &str, arg: &'a str)
-> Pin<Box<dyn Future<Output=()> + 'a>>
{
if action.starts_with("one") {
Box::pin(some_async_func_one(arg))
} else {
Box::pin(some_async_func_two(arg))
}
}
Тем не менее, это всего лишь одна куча, поэтому я настоятельно рекомендую возвращать будущее. Единственный сценарий, который я могу себе представить, где предыдущее решение лучше, - это когда вы хотите сохранить коробочное закрытие где-нибудь и использовать его много раз. В этом случае чрезмерное распределение происходит только один раз, и вы экономите некоторое время ЦП, отправляя вызов на основе action
только один раз - при закрытии.