If you can generalize the signature to
async fn foo<A: Serialize, B, S: for<'a> DeserializeSeed<'a, Value = B>>(args: A, seed: S) -> B {
todo!()
}
then it should become possible:
use std::{future::Future, pin::Pin};
use serde::{de::DeserializeSeed, Serialize};
use m::*;
#[allow(unused_variables)]
async fn foo<A: Serialize, B, S: for<'a> DeserializeSeed<'a, Value = B>>(args: A, seed: S) -> B {
todo!()
}
struct Foo;
impl Callback for Foo {
fn call<'lt, A: 'lt, B: 'lt, S: 'lt>(
&'lt self,
args: A,
seed: S,
) -> Pin<Box<dyn Future<Output = B> + 'lt>>
where
A: Serialize,
S: for<'a> DeserializeSeed<'a, Value = B>,
{
Box::pin(foo(args, seed))
}
}
// demonstration:
async fn foo_indirectly<A: Serialize, B, S: for<'a> DeserializeSeed<'a, Value = B>>(args: A, seed: S) -> B {
let erased: ErasedCallback<'static> = Box::new(Foo).into();
erased.call(args, seed).await
}
async fn foo_indirectly_less_generic_usage<A: Serialize, B: DeserializeOwned>(args: A) -> B {
foo_indirectly(args, PhantomData::<B>).await
}
// implementation:
mod m {
use erased_serde as e;
use serde as s;
use std::{future::Future, pin::Pin};
pub trait Callback {
fn call<'lt, A: 'lt, B: 'lt, S: 'lt>(
&'lt self,
args: A,
seed: S,
) -> Pin<Box<dyn Future<Output = B> + 'lt>>
where
A: s::Serialize,
S: for<'a> s::de::DeserializeSeed<'a, Value = B>;
}
pub struct ErasedCallback<'lt>(Box<dyn DynCallback + 'lt>);
trait DeserializeSeedInPlace<'a> {
fn deserialize_in_place(&mut self, d: &mut dyn e::Deserializer<'a>)
-> Result<(), e::Error>;
}
struct DeserializationWrapper<D, S>(Option<S>, Option<D>);
impl<'a, D, S: s::de::DeserializeSeed<'a, Value = D>> DeserializeSeedInPlace<'a>
for DeserializationWrapper<D, S>
{
fn deserialize_in_place(
&mut self,
d: &mut dyn e::Deserializer<'a>,
) -> Result<(), e::Error> {
self.1 = Some(self.0.take().unwrap().deserialize(d)?);
Ok(())
}
}
impl<'de> s::de::DeserializeSeed<'de> for &mut dyn for<'a> DeserializeSeedInPlace<'a> {
type Value = ();
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: s::Deserializer<'de>,
{
self.deserialize_in_place(&mut <dyn e::Deserializer>::erase(deserializer))
.map_err(<D::Error as s::de::Error>::custom)
}
}
trait DynCallback {
fn dyn_call<'lt>(
&'lt self,
args: &'lt dyn e::Serialize,
seed_and_result_slots: &'lt mut dyn for<'a> DeserializeSeedInPlace<'a>,
) -> Pin<Box<dyn Future<Output = ()> + 'lt>>;
}
impl<C: Callback> DynCallback for C {
fn dyn_call<'lt>(
&'lt self,
args: &'lt dyn e::Serialize,
seed_and_result_slots: &'lt mut dyn for<'a> DeserializeSeedInPlace<'a>,
) -> Pin<Box<dyn Future<Output = ()> + 'lt>> {
Box::pin(self.call(args, seed_and_result_slots))
}
}
impl Callback for ErasedCallback<'_> {
fn call<'lt, A: 'lt, B: 'lt, S: 'lt>(
&'lt self,
args: A,
seed: S,
) -> Pin<Box<dyn Future<Output = B> + 'lt>>
where
A: s::Serialize,
S: for<'a> s::de::DeserializeSeed<'a, Value = B>,
{
Box::pin(async move {
let mut slots = DeserializationWrapper(Some(seed), None);
self.0.dyn_call(&args, &mut slots).await;
slots.1.unwrap()
})
}
}
impl<'lt, C: Callback + 'lt> From<Box<C>> for ErasedCallback<'lt> {
fn from(x: Box<C>) -> Self {
ErasedCallback(x)
}
}
}
This compiles, I haven’t tested it yet though, because I didn’t have any test case implementation of foo
.