Idea: New feature flag to produce type-only crates to solve the orphan rule

I'm struggling with the topic name, and even what forum is the best place to put this, if someone can give me a better idea of what to call this and where to put it, I'm all ears!

Problem outline:

I'm creating a new crate with a new trait that I've defined. I want to supply a derive macro for my trait. However, like many common traits, the trait is recursive; e.g., when you derive Clone, the derive macro produces code assuming that all elements of the type it's operating on are also Clone, and it assumes this recursively. As soon as it hits an object that hasn't had it derived on it, then you'll get a compile error, and will need to manually implement the trait.

By itself, this isn't a big deal, but because of the orphan rule (1, 2, 3), there are restrictions on crate authors implementing foreign traits on foreign types, so to be a nice crate author, I want to supply implementations of my trait on some common libraries (e.g. core, std, the top few most downloaded on so that others don't have to do any newtype trickery just to get around the orphan rule.

So how do I do this today? As far as I know, it's a manual process, looking into each crate that you're interested in implementing your trait on, and producing implementations for everything of interest. This is slow and error prone for all of the following reasons:

  1. Large libraries like core and std can have a very large number of types defined within them. If you accidentally miss one then you've got a bug in your crate. This is irritating.
  2. Some crates have intricate configuration rules. To see an example, read through the contents of std::src::sys. Implementing a trait manually may require you to duplicate that configuration logic so that you can customize the trait implementation as needed.
  3. Some crates produce new types at compile time. For example, if you're using the derive_builder crate, then there will be types that don't exist in what you've personally typed in.
  4. Possible issues with (I don't really know about this, I don't use it, I'm just aware of it).

Taken together, I end up with the following issues:

  • Point 1 means bug reports all day, every day, because of yet another type that you missed.
  • Points 1 & 2 mean even more bug reports, probably on systems I don't have direct access to, so I have to rely on my CI setup to catch the bugs, and which means that my CI has to be as complete as possible, and which means that what would normally be a fast turnaround time slows to a crawl.
  • Point 3 means that there may be types defined that I'm not even aware of (cargo-expand helps with this, but produces a lot of output).
  • Point 4 may mean that cargo-expand is powerless.

My idea

rustc is already able to figure out what types there are in a crate; if it weren't able to, it wouldn't be able to compile the crate. Is there a way to leverage this ability to produce a new crate from a crate that has same configuration flags, etc., but with just the defined types in place? The newly produced crate could be included in other crates behind a feature flag, reducing its footprint within the compiled binary. E.g., while my crate has trait implementations for serde, unless you include serde in your crate you won't have to compile all the trait implementations I have in my crate.

I don't know if it could handle points 3 and 4 above, but if it were able to, then it would pretty solve the headaches the orphan rule produces.

Normally this is done with optional dependencies, and features. I.e. many crates have serde feature. The crate providing the traits normally doesn't provide implementations for types outside of std. The only exception I can think of is if you expect your crate to he heavily used with another, then you may provide implementations for that crate.

The truth is, I don't know what all my crate would be used with. std is a given, but there could be other crates. And while I could submit PRs to those other crates to get them to implement my trait, it would rapidly become a burden for them. I'd rather implement everything within my crate, at least initially, and if I can prove its utility to the maintainers of other crates, then they might decide to move the functionality into their own crates.