#Can you derive(Serialize) only if a dependent type T is Serialize?

15 messages · Page 1 of 1 (latest)

soft sierra
#

I have this struct

pub struct Grid<T> {
    tiles: Vec<T>,
    default: T,
    width: i32,
    height: i32,
}

Which is used both for T's that are Serialize and T's that aren't. Is there a way to get the functionality of derive(Serialize) but only if T: Serialize?
Ideally, I don't want to implement Serialize by hand at all.

left quail
#

I think this is how it works by default.

soft sierra
#

oh wow

#

I can't believe this just worked

#

... I didn't try to just do it 😛

#

I just assumed that #derive would try to generate the trait and fail if T isn't Serialize

#

okay how do I close this shameful thread? Thanks though 🙂

left quail
#

Idk if you can, but it'll expire after a while automatically

red oxide
#

There is one case of an issue I've run into: It's smart enough to work it out if it just requires the derived trait, but if it requires some other trait for checking equality (e.g. HashMap requiring BuildHasher be implemented for its S type), you'll have to implement it by hand...

tawdry shard
#

yeah there’s all sorts of limitations with derive macros

#

thankfully they work 95% of the time

untold hornet
#

Tbh you can make it work 100% of the time by tricking it into writing the bounds for the field types rather than the generic parameters (which the derive since it's kind of a more cautious default overall, but you can decide to push things further than the default)

#

Another option is to still use the stdlib derive, but with a helper internal data structure:

#
#[derive(YourDerivesHere)]
pub struct GridFields<Tiles, Default, Width, Height> {
    tiles: Tiles,
    default: Default,
    width: Width,
    height: Height,
}

pub type Grid<T> = GridFields<
    Vec<T>,
    T,
    i32,
    i32,
>;

this tricks the derive into writing:

impl<…> Trait for Grid<T>
where
    // Bounds of fields!!
    Vec<T> : Trait,
    T : Trait,
    i32 : Trait,
    i32 : Trait,
{ 

rather than:

impl<…> Trait for Grid<T>
where
    // Bounds on generics by default
    T : Trait,
{

so it works even with more complex trait implications (such as the aforementioned HashMap example)