I am implementing a function in Rust that take a callback function as an argument. As far as I learned, I can use Fn trait bound to do that.
But now I want this callback function to be async, and I want to define a plain async fn function and use it as the argument, just like how Axum works.
Following is what have I have done so far:
pub struct Engine<T, Before, BeforeFut>
where
Before: for<'a> Fn(&'a mut Option<T>) -> BeforeFut,
BeforeFut: Future<Output = u32>,
{
context: Option<T>,
callback: Option<Before>,
}
impl<T, Before, BeforeFut> Engine<T, Before, BeforeFut>
where
Before: for<'a> Fn(&'a mut Option<T>) -> BeforeFut,
BeforeFut: Future<Output = u32>,
{
pub fn set_callback(mut self, callback: Before) -> Self {
self.callback = Some(callback);
self
}
pub async fn run(mut self) {
if let Some(f) = self.callback {
f(&mut self.context).await;
}
}
}
async fn the_callback(context: &mut Option<TheContext>) -> u32 {
context.as_ref().unwrap().counter + 1
}
struct TheContext {
pub counter: u32,
}
#[tokio::main]
async fn main() {
let mut engine = Engine {
context: Some(TheContext { counter: 0 }),
callback: Some(the_callback),
};
let result = engine.run().await;
assert_eq!(result, 2);
}
This minimal code failed to compile:
error[E0599]: no method named `run` found for struct `Engine<TheContext, fn(&mut Option<TheContext>) -> ... {the_callback}, ...>` in the current scope
--> src/main.rs:44:25
|
2 | pub struct Engine<T, Before, BeforeFut>
| --------------------------------------- method `run` not found for this struct
...
44 | let result = engine.run().await;
| ^^^ method not found in `Engine<TheContext, fn(&mut Option<TheContext>) -> ... {the_callback}, ...>`
|
= note: the method was found for
- `Engine<T, Before, BeforeFut>`
This message confuses me since the method run is defined.
Your current error is shadowing the original issue, namely that the_callback()
does not implement for<'a> Fn(&'a mut Option<T>) -> BeforeFut
:
pub struct Engine<T, Before, BeforeFut>
where
Before: for<'a> Fn(&'a mut Option<T>) -> BeforeFut,
BeforeFut: Future<Output = u32>,
{
context: Option<T>,
callback: Option<Before>,
}
impl<T, Before, BeforeFut> Engine<T, Before, BeforeFut>
where
Before: for<'a> Fn(&'a mut Option<T>) -> BeforeFut,
BeforeFut: Future<Output = u32>,
{
pub fn set_callback(mut self, callback: Before) -> Self {
self.callback = Some(callback);
self
}
pub async fn run(mut self) {
if let Some(f) = self.callback {
f(&mut self.context).await;
}
}
}
async fn the_callback(context: &mut Option<TheContext>) -> u32 {
context.as_ref().unwrap().counter + 1
}
struct TheContext {
pub counter: u32,
}
#[tokio::main]
async fn main() {
let mut engine = Engine {
context: Some(TheContext { counter: 0 }),
callback: Some(the_callback),
};
//let result = engine.run().await;
//assert_eq!(result, 2);
}
Compiling playground v0.0.1 (/playground)
warning: unused variable: `engine`
--> src/main.rs:37:13
|
37 | let mut engine = Engine {
| ^^^^^^ help: if this is intentional, prefix it with an underscore: `_engine`
|
= note: `#[warn(unused_variables)]` on by default
warning: variable does not need to be mutable
--> src/main.rs:37:9
|
37 | let mut engine = Engine {
| ----^^^^^^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` on by default
error[E0308]: mismatched types
--> src/main.rs:37:22
|
37 | let mut engine = Engine {
| ______________________^
38 | | context: Some(TheContext { counter: 0 }),
39 | | callback: Some(the_callback),
40 | | };
| |_____^ one type is more general than the other
|
= note: expected opaque type `impl for<'a> Future<Output = u32>`
found opaque type `impl Future<Output = u32>`
= note: distinct uses of `impl Trait` result in different opaque types
note: the lifetime requirement is introduced here
--> src/main.rs:3:46
|
3 | Before: for<'a> Fn(&'a mut Option<T>) -> BeforeFut,
| ^^^^^^^^^
For more information about this error, try `rustc --explain E0308`.
warning: `playground` (bin "playground") generated 2 warnings
error: could not compile `playground` (bin "playground") due to 1 previous error; 2 warnings emitted
You can use the new AsyncFn
trait to specify your callback type:
pub struct Engine<T, Before>
where
Before: for<'a> AsyncFn(&'a mut Option<T>) -> u32,
{
context: Option<T>,
callback: Option<Before>,
}
impl<T, Before> Engine<T, Before>
where
Before: for<'a> AsyncFn(&'a mut Option<T>) -> u32,
{
pub fn set_callback(mut self, callback: Before) -> Self {
self.callback = Some(callback);
self
}
pub async fn run(mut self) {
if let Some(f) = self.callback {
f(&mut self.context).await;
}
}
}
async fn the_callback(context: &mut Option<TheContext>) -> u32 {
context.as_ref().unwrap().counter + 1
}
struct TheContext {
pub counter: u32,
}
#[tokio::main]
async fn main() {
let engine = Engine {
context: Some(TheContext { counter: 0 }),
callback: Some(the_callback),
};
let result = engine.run().await;
assert_eq!(result, ());
}