Footprint of a cargo dependency if you only use a single type?

Occasionally I just need to reference the type defined in a separate crate, but by adding that crate to my Cargo.toml I noticed my compile time shoots way up.

Some APIs offer their types in a separate crate, e.g.: Rust Package Registry

But most require you to pull in the full crate. How can I optimize my build time by depending on just a single struct, enum, or type alias from another crate without compiling it entirely?

can tell you which crates cost the most, especially generic types.

Additionally, enable LTO to remove unused code. Otherwise the executable may contain unused code of dependencies.

LTO is an odd thing to suggest to the person complaining that their compile time shoots way up.

The answer to the OP is that using a single type from a crate means that the whole crate and all of its dependencies must be compiled. In fact, I believe that simply listing the crate as a dependency in Cargo.toml is enough, since Cargo orchestrates the build process, but doesn't have visibility into the Rust source itself and doesn't know which parts of a crate are used.

In general, if the dependency isn't under your control, then there is nothing you can do to avoid blowing up compile times. Your only option is to avoid pulling in that dependency. If the single type you used is re-exported from some other crate, consider linking that one instead (e.g. use types from rand-core rather than rand). Perhaps parts of the dependency are feature-gated. You can disable those features to cut some compile time (note that the features may be default ones, so you'd need to set default-features = false for the dependency).


You might also consider gating the crates behind features in your own crate, if you only need them occasionally. That's going to depend on what you're doing, of course, but if it's a big bloat and only used for a small set of functionality in your crate, it might be worth it.

1 Like

I was previously instantiating objects from a different crate - so that only that crate needed the dependencies in order to do the instantiating.

Then, my main crate never explicitly writes the type, so it doesn't need those dependencies. But recently, I needed to instantiate these objects inside my main crate and put them all inside a struct and so my perfect work around crumbled since the struct would get quite messy with 5 generic arguments.

It's these rare occasions where I wish I could just turn off typing and save some much needed compile time :slight_smile: (call everything Object like in Java...)

thank you, I linked to polars-error which is an example crate that only stores types for polars. It's lovely, I wish Rust saw this coming the community all set this precedent early on to keep types within arms reach either via features or separate crates.

That said, these types aren't doing much for me as the objects I'm instantiating aren't owned by me or susceptible to bugs. I'd love to just call them Object like if this were Java and then cast them appropriately in downstream crates that actually need to know their types I also depend on :slight_smile:

Another thought I had is if sealed struct/traits in Rust could automatically be severable - just by telling Cargo.toml the class path to them? That would be a nice workaround...

You could always put those types in a Box<dyn Any>, if they are not generic (including over lifetimes). That's basically what a Java Object does. That doesn't help with compile times, since you'd neee to work with the corresponding concrete type anyway. Although if you're just taking the external type as input at some point and passing it around without calling methods, taking Box<dyn Any> should work.

1 Like

True, but actually I just realized I'm being foolish:

The part to my code that processes the concrete type (a separate crate) can create a wrapper struct:

pub struct Opaque(inner: Concrete)

Then I just reference Opaque and when the other crate receives that type it grabs the inner. Thanks for helping me think about this more.

...That said I still think it'd be slick if Cargo.toml could fetch sealed types. "Sealed" as in everything that defines this type is in the same .rs file. That way you can dissect a crate beyond features when you're really trying to save compile time.

1 Like