A partial remedy for orphan impl troubles: procedural blanket impls


#1

Recently procedural macros are getting much attention. One usage of such macros is custom derive, that allows defining macros that allow deriving trait implementations easily. (See https://github.com/rust-lang/rfcs/pull/1681)

There’s also a problem with traits: the trait coherence rules, which prevent crates from having incompatible trait implementations with each other, prevent also some useful patterns. Let’s say that I use the library Serde for serialization. I also use some upstream crate Foo of which I have no control over. I want to serialize struct Bar from crate Foo, but the author haven’t had the foresight to derive or implement the trait that allows this for the struct.

The coherence rules are there for a reason: if anybody could implement the serialization traits, there could be conflicting implementations introduced by third party libraries, and thus linking together some unfortunate combination of libraries could break the build.

I think we could remedy the situation by allowing the trait author to provide deriving macros for procedural blanket impls. The idea is that the impl would be derived for any struct/enum without even the struct/enum authors opting in, unlike with the current derived impls. The struct/enum author could, however, provide their own impl that overrides the blanket impl, or opt out from derivations.

The derivation would happen at the downstream crate that imports and uses a combination of a trait and a struct/enum. I think that would help at least with the coherence problems with Serde and some other crates that have algorithmically derivable impls.

I originally wrote about this idea in https://github.com/rust-lang/rfcs/issues/1553, but there wasn’t much discussion. What do you all think?


#2

I don’t think you can naively manipulate arbitrary structs without introducing ways to violate memory safety in safe code. There’s no way for a trait author to handle the untyped invariants that struct fields have.

For example, a naive implementation of Serialize/Deserialize serializes/deserializes a struct to an object with the same field names. This would serialize a vec to { data: [], len: 0, capacity: 0 }, and would also accept constructing a vec from { data: [], len: 100, capacity: 50 }, which would be bad.

This isn’t even getting to the issue of correctness - i.e. that is not what Vec should be serialized to.


#3

I think that the privacy rules should continue to apply – the deriving macro shouldn’t be able to access fields that aren’t public. But that’s indeed a problem, if the macros process just string of tokens. Does a concept of “private field” even exist at that level?


#4

I never answered this question but if the macro is generated in the user’s crate, rather than the module where the type is defined, then the generated code can’t access private fields. However, my experience is that most structs have private fields, so if you can’t access them this seems to be limited in usefulness.