When trying to write a function that receives a async closure, I found it is a little bit tricky.
I know correct version is
pub async fn f1<F, Fut>(f: F) -> u32
where
F: Fn(u32) -> Fut,
Fut: Future<Output = u32>,
{
f(9u32).await
}
However, the where
clause in the Rust doc saying that it is just a convenient way to express those type, so I tried to remove the where clause like this:
pub async fn f2(f: Fn(u32) -> Future<Output = u32>) -> u32 {
f(9u32).await
}
To my surprise, Rust refused to compile it, saying that
error: trait objects must include the dyn keyword
Anyone know what is the correct way to remove the where
clause?
Any magic is played here by the where
clause?
CodePudding user response:
In your first snippet you have to constraints:
F
must implementFn(u32) -> Fut
.Fut
must implementFuture<Output = u32>
.
But in your second snippet there are no constraints. The syntax for an inline constraint is with the impl
keyword:
pub async fn f2(f: impl Fn(u32) -> Future<Output = u32>) -> u32 {
f(9u32).await
}
This doesn't work either because the impl
applies to the Fn
trait only, not to the return trait, so the compiler complains that a naked trait is not allowed, that you should add dyn
to it.
Bad advice, because:
pub async fn f2(f: impl Fn(u32) -> dyn Future<Output = u32>) -> u32 {
f(9u32).await
}
will not work either, because dyn Trait
types are unsized and cannot be returned.
You could try another impl
there, that usually works in return types but:
pub async fn f2(f: impl Fn(u32) -> impl Future<Output = u32>) -> u32 {
f(9u32).await
}
impl Trait` not allowed outside of function and method return types
The only syntax I know of to constraint the return type of an Fn
trait is with the where
syntax:
pub async fn f2<Fut>(f: impl Fn(u32) -> Fut) -> u32
where
Fut: Future<Output = u32>,
{
f(9u32).await
}
But now that you are in full where
mode, I see little reason to keep the inline constraint. Personally I consider bad style having both syntaxes on the same function.
CodePudding user response:
Assuming you're referencing Rust by Example:
The example there doesn't use trait objects (e.g. foo: Box<dyn SomeTrait>
) or existential types (e.g. foo: impl SomeTrait
) but adds bounds to generic types at the declaration site:
impl <A: TraitB TraitC, D: TraitE TraitF> MyTrait<A, D> for YourType {}
// Expressing bounds with a `where` clause
impl <A, D> MyTrait<A, D> for YourType where
A: TraitB TraitC,
D: TraitE TraitF {}
In your case, you'd be writing:
pub async fn f2<F: Fn(u32) -> Fut, Fut: std::future::Future<Output = u32>>(f: F) -> u32 {
f(9u32).await
}
instead of putting the Fn(u32) -> Future<Output = u32>
bound as the type of f
. Note that you also need to declare a generic type Fut: Future<Output = u32>
for the returned future from the closure, otherwise you'd be returning a raw trait object again.
As mentioned by @rodrigo, it's also possible to use the impl SomeTrait
notation to entirely remove the generic type parameter from the function.