Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use smart pointers? #23

Open
vlovich opened this issue May 12, 2024 · 7 comments
Open

How to use smart pointers? #23

vlovich opened this issue May 12, 2024 · 7 comments

Comments

@vlovich
Copy link

vlovich commented May 12, 2024

If I want to have a Serialize that contains an Rc<Vec>, is there a way to do this?

#[alkahest(Formula)]
struct F {
  values: Vec<u8>,
}

#[alkahest(Serialize<F>, Deserialize<'_, F>)]
struct S {
  values: Rc<Vec<u8>> // How do I map this so that an Rc is constructed around the deserialized Vec / serializes the interior of the Rc?
}
@zakarumych
Copy link
Owner

You may need to add generic implementation for this smartpointer.
Look at implementation for Box

@vlovich
Copy link
Author

vlovich commented May 13, 2024

Where do I find the implementation? The only place Box comes up as a string in the code is in the roundtrip test.

@vlovich
Copy link
Author

vlovich commented May 16, 2024

Not sure if it's the cleanest way but here's what I came up with. The annoying bit is that any struct that uses Rc now needs to be broken out into an explicit formula unless there's an obvious way to connect RcFormula to Rc or I should be doing something else? Certainly nothing as elegant as Box & I couldn't find where in the code Box<T> is managed. The other problem is that deserialize_in_place is on the same trait as deserialize which means I can't define deserialize and a separate deserialize_in_place that requires T: Default, so I have to return an error.

#[derive(Formula)]
struct RcFormula<T>(T);

impl<T: SerializeRef<T> + Formula> SerializeRef<RcFormula<T>> for std::rc::Rc<T> {
    fn serialize<B>(
        &self,
        sizes: &mut alkahest::advanced::Sizes,
        buffer: B,
    ) -> std::result::Result<(), B::Error>
    where
        B: alkahest::advanced::Buffer,
    {
        <T as SerializeRef<T>>::serialize(self.as_ref(), sizes, buffer)
    }

    fn size_hint(&self) -> Option<alkahest::advanced::Sizes> {
        <T as SerializeRef<T>>::size_hint(self.as_ref())
    }
}

impl<T: Serialize<T> + Formula> Serialize<RcFormula<T>> for std::rc::Rc<T> {
    fn serialize<B>(
        self,
        sizes: &mut alkahest::advanced::Sizes,
        buffer: B,
    ) -> std::result::Result<(), B::Error>
    where
        B: alkahest::advanced::Buffer,
    {
        if let Some(inner) = std::rc::Rc::into_inner(self) {
            <T as Serialize<T>>::serialize(inner, sizes, buffer)
        } else {
            panic!("Not a unique reference and can't return an error?")
        }
    }

    fn size_hint(&self) -> Option<alkahest::advanced::Sizes> {
        <T as Serialize<T>>::size_hint(self.as_ref())
    }
}

impl<'de, T: Deserialize<'de, T> + Formula> Deserialize<'de, RcFormula<T>> for std::rc::Rc<T> {
    fn deserialize(
        deserializer: alkahest::advanced::Deserializer<'de>,
    ) -> std::result::Result<Self, alkahest::DeserializeError>
    where
        Self: Sized,
    {
        Ok(std::rc::Rc::new(<T as Deserialize<T>>::deserialize(
            deserializer,
        )?))
    }

    fn deserialize_in_place(
        &mut self,
        _deserializer: alkahest::advanced::Deserializer<'de>,
    ) -> std::result::Result<(), alkahest::DeserializeError> {
        // T might not implement Default so there's no way to get a &mut T from self.
        Err(alkahest::DeserializeError::Incompatible)
    }
}

@zakarumych
Copy link
Owner

I've added implementation for Box, Rc and Arc in
7a45d26

@vlovich
Copy link
Author

vlovich commented May 16, 2024

Ow wow! Did not know expect that, thanks! I also may have 3p smart pointers (eg I use hybrid_rc although not sure it'll come up here necessarily). I'm assuming there's no good way to do this generically since I can't impl Serialize/Deserialize for external crates, so I'll need to write wrappers or explicit formulas instead?

@zakarumych
Copy link
Owner

Take note that Box cannot be a generic Formula due to being #[fundamental] which allows downstream crates to implement Formula for Box<DownstreamCrateLocalType> and this causes potential conflict disallowed by Rust.
So if you need to serialize Box<T> field you'll need a separate type to derive Formula where field is just F where T: Serialize<F>.

Like this:

#[derive(alkahest_proc::Formula)]
struct Foo {
    a: u32,
}

#[alkahest(SerializeRef<Foo>, Deserialize<'_, Foo>)]
struct FooWithBox {
    a: Box<u32>,
}

@zakarumych
Copy link
Owner

zakarumych commented May 16, 2024

hybrid_rc

You can add impls to this crate with optional dependency

Or in their crate instead :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants