I have a dynamic trait Action which has the handle() method which accepts an App<T> argument. This doesn't work because I get the following error:
17 | action: Box<dyn Action + 'static>,
| ^^^^^^^^^^^^^^^^^^^^ `Action` is not dyn compatible
|
note: for a trait to be dyn compatible it needs to allow building a vtable
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
--> framework/src/action/action.rs:10:14
|
9 | pub trait Action: Send + Sync + Debug {
| ------ this trait is not dyn compatible...
10 | async fn handle<T>(
| ^^^^^^ ...because method `handle` has generic type parameters
= help: consider moving `handle` to another trait
What I'm trying to achieve is create a crate with a struct that can accept an App<T> struct where T is AppState from the user. However, this is causing a conflict with the dyn Action trait, which accepts that AppState struct.
Is there a way around this?
What's the full signature? If it just accepts an arg: App<T> and doesn't return it, for example, there's nothing very useful that can be done with the T inside the method. So you could have some method on App to transform it to an App<()> or such.
Otherwise you typically need to work in more type erasure, like App<Box<dyn Any>> or the like.
Incidentally, async fn is also not dyn-compatible, so you'll probably end up with something like
fn handle(...) -> Pin<Box<dyn Future<Output = ()> + Send + Sync '_>>
(or some use of macros that effectively does that).
I'm using the async-trait
#[async_trait]
pub trait Action: Send + Sync + Debug {
async fn handle<T>(
&self,
app: &App<T>,
request: HttpRequest,
) -> Result<Box<dyn Responsable>, HttpError>;
}
Whether it uses App<T> or not depends on the action. Some may use it, others may not. I'll see what I can do with Box and report back.
What I mean is, say I'm implementing your trait...
#[async_trait]
impl Action for Whatever {
async fn handle<T>(
&self,
app: &App<T>,
request: HttpRequest,
) -> Result<Box<dyn Responsable>, HttpError> {
// Considering this method body
}
}
...I can only use properties of T which are requirements on the generic. Here, the only requirement is that T: Sized. There's nothing really useful that allows me to do in the method body. So the only useful things I can do with app are things that likewise don't depend on any particular T.
So find an alternative that gets T out of the signature; probably some way to convert from &App<T> to something without T. That could be a method to convert &App<T> to &App<ConcreteType> or NotApp<'_> or such. Or another way would be to put all the useful things you can do with "&App<T> for any T" into a trait and take a &dyn AppTrait (which &App<T> could coerce to).
So I created this type with some extras to satisfy the compiler since it'll be sent across threads (hopefully only immutably).
type AppState = Box<dyn Any + Send + Sync>;
And in the Action struct.
let state: &AppState = &app.state.downcast_ref().unwrap();
This seems to have satisfied the compiler.
Appreciate the help!