Remix.run Logo
ekidd 14 hours ago

There's a well-known (and frequently encouraged) workaround for the orphan rule: Create a wrapper type.

Let's say you have one library with:

    pub struct TypeWithSomeSerialization { /* public fields here */ }
And you want to define a custom serialization. In this case, you can write:

    pub struct TypeWithDifferentSerialization(TypeWithSomeSerialization)
Then you just implement Serialize and Deserialize for TypeWithDifferentSerialization.

This cover most occasional cases where you need to work around the orphan rule. And semantically, it's pretty reasonable: If a type behaves differently, then it really isn't the same type.

The alternative is to have a situation where you have library A define a data type, library B define an interface, and library C implement the interface from B for the type from A. Very few languages actually allow this, because you run into the problem where library D tries to do the same thing library C did, but does it differently. There are workarounds, but they add complexity and confusion, which may not be worth it.

dhosek 14 hours ago | parent [-]

The gotcha is what happens when TypeWithSomeSerialization is not something you’re using directly but is contained within SomeOtherTypeWithSomeSerialization which you are using directly. Then things get messy.

deathanatos 9 hours ago | parent | next [-]

We can't say with certainty how an unspecified in-the-future library might work, so I'm going to use serde as a stand-in.

You can implement `Serialize` for a wrapper type and still serialize `SomeOtherTypeWithSomeSerialization` (which might be used by the type being wrapper directly or indirectly) differently. It might not be derivable, of course, but "I don't want the default" sort of makes that a given.

bigfishrunning 11 hours ago | parent | prev [-]

In that case, wrap a reference maybe?

    pub struct TypeWithDifferentSerialization(&TypeWithSomeSerialization)