Remix.run Logo
▲ Lvl999Noob an hour ago

You can do it at compile time. Or more likely, link time. If the implementation is ever actually used, it is kept in the binary. Otherwise it is used. The compiler can do the same, keeping all derives as just markers until it finds a place that actually uses it then firing off a background worker to compile the derive impl.

▲K0nserv 14 minutes ago | parent | next [-]

The compiler already does this.

Compare https://rust.godbolt.org/z/osMY8777W and https://rust.godbolt.org/z/azGjbbzc8. I'm not sure why the inlining doesn't happen in the case when we do use the Debug implementation though.

▲saghm 43 minutes ago | parent | prev [-]

My instinct is that this would end up being more costly than it's worth. You'd be essentially adding extra bookkeeping and logic for every single type that needs to remain alive as long as the type is still possible to reference.

Moreover, how would you deal with downstream usage by dependents? Should I be able to make my dependency's type implement Debug (which is at least in spirit a violation of the orphan rule, and would make the bookkeeping/extra logic described above explode for any non-trivial dependency tree)?

People already find the Rust compiler too slow. If there's budget for adding more expensive checks, I don't think I'd want it to be spent on something like this.

▲vlovich123 24 minutes ago | parent [-]

The vast majority of Debug traits never get used. All the standard library containers (collections, smart pointers, refcell, etc) have Debug traits auto-emitted if the underlying type has Debug. Even if the type never gets formatted.

I really doubt lazily code genning the Debug trait makes things worse or is super expensive to track. The main tension is more how this interplays with LTO (which I'm guessing would be unable to do this) and designing a generic language feature here for macros.

A rough sketch of the idea would be that you can annotate functions with #[lazy] and the Rust compiler knows to save off the function body for later and only keep the function definition around (no codegen). Then if it does need to codegen because it gets invoked from a non-#[lazy] function, the function is treated as inlineable and #[cold] so that if it's not inlined it's put in a cold region of the binary. It's non-trivial because you somehow need to put the codegen back into the original crate's object file which is probably tricky.

I don't understand the concern regarding the orphan rule or "downstream usage by dependents" - that's not relevant here and no language semantics change.