В функциональном программировании существует общая закономерность: вы берете функцию, используете ее и возвращаете другую функцию (часто с той же сигнатурой).
В Python этот шаблон настолько распространен, что использует особый синтаксис декоратора @
.
Я пытаюсь реализовать этот подход в Rust, но на нетривиальных примерах у меня ничего не получается.
Спасибо за этот ответ, этот тривиальный синхронный пример работает:
fn wrapper<T, V, F>(f: F) -> impl Fn(T) -> V where
F: Fn(T) -> V {
move |x| f(x)
}
fn my_func_1(x: u16) -> u16 {
x * 2
}
fn my_func_2(x: Vec<i32>) -> i32 {
x.iter().sum()
}
fn main() {
let res = wrapper(my_func_1)(42);
let res = wrapper(my_func_2)(vec![1,2,3]);
}
Но в асинхронном контексте все становится запутанным! В этом случае мне не удалось реализовать ту же функциональность. Но благодаря этому ответу я принял такое тривиальное решение:
use std::future::Future;
fn wrapper<T, V, F, Fut>(f: F) -> impl Fn(T) -> Fut where
F: Fn(T) -> Fut,
Fut: Future<Output=V> {
f
}
async fn my_func_1(x: u16) -> u16 {
x * 2
}
async fn my_func_2(x: Vec<i32>) -> i32 {
x.iter().sum()
}
#[tokio::main]
async fn main() {
let res = wrapper(my_func_1)(42).await;
println!("my_func_1: res = {:?}", res);
let res = wrapper(my_func_2)(vec![1,2,3]).await;
println!("my_func_2: res = {:?}", res);
}
По сравнению с синхронной реализацией в оболочке async
я не могу создать новую функцию для возврата.
Что-то вроде этого:
fn wrapper<T, V, F, Fut>(f: F) -> impl Fn(T) -> Fut where
F: Fn(T) -> Fut,
Fut: Future<Output=V> {
move |x| async { f(x) }
}
завершается с ошибкой:
error[E0308]: mismatched types
--> src/main.rs:7:14
|
4 | fn wrapper<T, V, F, Fut>(f: F) -> impl Fn(T) -> Fut where
| --- expected this type parameter
...
7 | move |x| async { f(x) }
| ^^^^^^^^^^^^^^ expected type parameter `Fut`, found `async` block
|
= note: expected type parameter `Fut`
found `async` block `{async block@src/main.rs:7:14: 7:28}`
= help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `Fut`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `rust_test` (bin "rust_test") due to 1 previous error
Какие-либо предложения!?
-> impl Fn(T) -> Fut
означает функцию, которая при задании T
возвращает Fut
; примечание Fut
здесь является фиксированным типом. Мне не ясно, хотите ли вы, чтобы это оставалось таким (и в этом случае ошибка имеет смысл, поскольку вы не возвращаете Fut
из замыкания), или если вы хотите, чтобы это было другое будущее, чем Fut
, что тоже случается выведите V
и даже можете использовать F
при этом.
На самом деле вам нужно что-то вроде -> impl Fn(T) -> impl Future<Output = V>
, но этот синтаксис (в настоящее время) запрещен.
Вы помещаете Future
в другой асинхронный блок, поэтому эта функция:
fn wrapper<T, V, F, Fut>(f: F) -> impl Fn(T) -> ?
where
F: Fn(T) -> Fut,
Fut: Future<Output = V>,
{
move |x| async { f(x) }
}
нужно будет вызывать с двумя await
:
let res = wrapper(my_func_1)(42).await.await;
Вероятно, это не то, чего вы хотите. Вы либо хотите, чтобы замыкание возвращало Future
напрямую:
fn wrapper<T, V, F, Fut>(f: F) -> impl Fn(T) -> Fut
where
F: Fn(T) -> Fut,
Fut: Future<Output = V>,
{
move |x| f(x)
}
или вы хотите дождаться Future
и вернуть новый тип Future
. К сожалению, вам нужна еще одна черта, чтобы выразить это.
fn wrapper<T, V, F, Fut>(f: F) -> impl FutureFn<T, Fut: Future<Output = V>>
where
F: Fn(T) -> Fut,
Fut: Future<Output = V>,
{
|x| async move { f(x).await }
}
trait FutureFn<T>: FnOnce(T) -> Self::Fut {
type Fut: Future;
}
impl<F, Fut, T> FutureFn<T> for F
where
F: FnOnce(T) -> Fut,
Fut: Future,
{
type Fut = Fut;
}
Подробнее об этом читайте в разделе Вызов общей асинхронной функции с (изменяемым) заимствованным аргументом. Обратите внимание: когда T
включает время жизни, это работает только тогда, когда вы передаете элемент асинхронной функции в wrapper
, а не замыкание.
@cdhowie Я определенно скопировал туда не то, оно должно просто вернуться Fut
Ах, это имеет больше смысла!
Спасибо, это делает работу!
Пожалуйста, опубликуйте полную ошибку из
cargo check
, а не из вашей IDE.