dtolnay / erased-serde

Type-erased Serialize, Serializer and Deserializer traits
Apache License 2.0
709 stars 36 forks source link

Make `erased_serde::ser::Ok` public (since it's obtainable through public API) #65

Closed Angelo13C closed 1 year ago

Angelo13C commented 1 year ago

As you can see from the code below, I only have access to the type that needs to be serialized when I create a new instance of new, so I need to store the serialize_fn:: inside the struct to call it later on.

struct Foo
{
    serialize_fn: fn(value: *mut u8) -> Result<erased_serde::Ok, erased_serde::Error>       //I can't do this because Ok is private
}

impl Foo
{
    fn new<T: Serialize>() -> Self
    {
        fn serializer_fn<T: Serialize>(value: *mut u8, serializer: &mut dyn erased_serde::Serializer) 
        {
            let value = value.cast::<T>();
            let value = unsafe { &*value };
            let result = value.serialize(serializer);
        }
        Self {
            serialize_fn: serializer_fn::<T>
        } 
    }

    pub fn serialize<S: Serializer>(&self, serializer: S, value: *mut u8) -> Option<Result<S::Ok, S::Error>>
        where <S as serde::Serializer>::Ok: 'static
    {
        let mut serializer = <dyn erased_serde::Serializer>::erase(serializer);
        let result = (serialize_fn)(value, serializer);

        //Here I need to map result to the Result<S::Ok, S::Error> (which is basically what the erased_serde::serialize function does under the hood)
        //But I can't do it because erased_serde::ser::Ok (and also erased_serde::any::Any) are private
        /*
        This is the code that would work
            unsafe {
                result
                    .unsafe_map(Ok::take)
                    .map_err(unerase)
            }
        */
    }
}

The problem is that I can't do that because erased_serde::Ok (as you can read from the code) is private in the erased-serde crate.

Can you please make it public (since it's the struct is obtainable in public API as you can see with my code, but it becomes unusable since it's private)?

Thanks :D

dtolnay commented 1 year ago

Your code has a lot of mistakes unrelated to Ok so it's tough to tell what you are doing, but I am very confident that using erased_serde's Ok type is not the right way.

Possibly something like this.

struct Foo {
    ty: unsafe fn(*const u8) -> *const dyn erased_serde::Serialize,
}

impl Foo {
    fn new<T>() -> Self
    where
        T: serde::Serialize + 'static,
    {
        unsafe fn ty<T>(ptr: *const u8) -> *const dyn erased_serde::Serialize
        where
            T: serde::Serialize + 'static,
        {
            ptr.cast::<T>()
        }
        Foo { ty: ty::<T> }
    }

    unsafe fn serialize<S>(&self, serializer: S, ptr: *const u8) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let value = unsafe { &*(self.ty)(ptr) };
        erased_serde::serialize(value, serializer)
    }
}

fn main() {
    let foo = Foo::new::<i32>();
    let ser = serde_json::value::Serializer;
    let ptr = &99i32 as *const i32 as *const u8;
    let result = unsafe { foo.serialize(ser, ptr) };
    println!("{}", result.unwrap());
}
Shatur commented 1 year ago

@dtolnay is it possible to erase generic serializer without exporting Ok? Because returing Serialize only works if you serialize the whole value or one of its field. If I need to serialize 2+ fields and now the whole struct, I can't return Serialize, I need to accept Serializer in my function.