Multiple libraries in a cargo project


#21

@jjpe

I don’t really understand this comment, but my best guess is that its misunderstood my post: this feature is not about allowing cycles between crates (which is absolutely impossible), but about the desire to separate something into a crate in order to get that guarantee that it does not have an interwoven dependency on other sections of your project. In other words, its about the desire to leverage properties of the crate dependency relationship to improve your code organization, not changing how crates work.


Your comments about compiler performance also seem to be off the point to me: a crate is the unit compilation. If you can break a section of your code into a sublibrary, you guarantee that you can compile it separately, and avoid recompiling it, from the other parts of your project that depend on it. Similarly, two sublibraries with no dependency relationship can be compiled in parallel. This is the same principle by which you avoid recompiling all of your dependencies today, and by which many of your dependencies can be compiled in parallel already.

Overall, there’s nothing in this proposal that would involve changing how crates work from how they work today, just allowing you to have multiple private library crates inside of a cargo package.


#22

To me, the big benefit of this proposal is allowing small parts of a larger package to get crate-like isolation without also making them separate packages on crates.io.

Separating them into crates has some very useful benefits:

  • the ability to use crate:: (in Rust 2018) to refer to the top level of this newly isolated unit
  • the ability to use pub(crate) (and soon crate) to restrict visibility to this newly isolated unit

Keeping them together on crates.io has some benefits too:

  • avoids creating public crates that you might not want to support separately
  • allows the use of shorter names that wouldn’t be available on crates.io
  • since the crates are all part of a single published package, opens up the possibility of expanding the definition of coherence to include the entire group of sub-libraries.

This combination of benefits, and a number of other related benefits makes me think that this is a big improvement. I also feel that the form of this proposal, as currently described, manages to achieve these goals without gratuitous differences from existing mechanisms, making it fit in nicely with what we already have and avoiding the creation of significant new non-compositional concepts.

I like it :smile:


#23

What if this sort of feature was enabled by having a private_members field in [workspace]. I.e.

[workspace]

members = ["path/to/public/member"]

private_members = ["path/to/private/member"]

Disallowing this for virtual manifests would probably be best, since the underlying implementation would add the content of the crate into the package of the root manifest.

Allowing only all private or all public workspaces (ignoring the root) would be analogous to the primary use case of the original proposal, though it would be possible to allow specifying workspaces with both and have various options for how to resolve which packages contained which private crates. I’d suggest just starting with the simple cases.

This seems to have the same implementation complexity (in terms of bundling multiple crates into a single package) but avoids having multiple ways of specifying the same sort of construct. cargo new and cargo init already handle workspace configuration (well, the docs claim they do), so adding a --private flag would allow people to get the right configuration without significantly changing their workflow.

I like the idea, mostly due to the benefits of crates for layering (dependency management), I just don’t want to have two completely distinct ways of specifying the same idea when adding 1 key to a config can accomplish the same thing.

Note, an alternative format could be:

[workspace]

[[members]]
path = "path/to/public/member"

[[members]]
path = "path/to/private/member"
private = true

This would result in members always being an array, either of strings or tables, but unfortunately, this would mostly just be more verbose and likely result in people confused by errors when they try to mix tables and strings or declare members twice, etc.