Better UX for `cargo install <command>` where the command is provided by another crate

It is quite common for Rust projects to be split into two crates: a library crate and a bin crate (with the bin crate depending on the library crate)[1], so that the CLI can have additional dependencies[2] and that both can have their own independent version.

This does however pose a dilemma for projects where the library and the binary are intended to share the same name:

  • For libraries the most intuitive installation procedure is if you can just run cargo add <library> and then import the library with use <library>;.
  • For applications the most intuitive installation procedure is cargo install <command> and then being able to just run <command>.

While Rust does let libraries and binaries with the same name co-exist (via lib.name or bin.name), to publish both crates on crates.io, one of the crates will have to be named differently than its primary artifact.

Personally the first of the above points takes priority for me, so if I have a project foobar, I'd name the library crate foobar and the CLI crate foobar-cli. This does however result in the following UX problem:

To obtain the foobar command, users will be tempted to run cargo install foobar, which will however inevitably fail with the error message:

$ cargo install foobar
    Updating crates.io index
error: there is nothing to install in `foobar v0.1.0`, because it has no binaries
`cargo install` is only for installing programs, and can't be used with libraries.
To use a library crate, add it as a dependency in a Cargo project instead.

My problem with this error message is that it does not tell the user about the existence of the foobar-cli crate. While cargo could check if such a crate existed, it of course should not assume anything about the purpose of crates just by their names.

So I was thinking how we could cleanly[3] solve this UX problem and thought we may want to introduce a new Cargo.toml field package.recommended-bin-crates so that in the above example the Cargo.toml from the foobar library crate could configure:

[package]
name = "foobar"
# etc.

recommended-bin-crates = ["foobar-cli"]

Which could then be picked up by the cargo install command when it detects that the crate doesn't have any binaries, to display a message such as the following to the user:

The developers of foobar suggest you may want to install foobar-cli instead.

What do you think about this dilemma / my idea? I'd be alright with writing an RFC for such a new recommended-bin-crates field, but I'm posting here first for some initial feedback :slight_smile:


  1. Prominent examples of that are diesel & diesel-cli or wasm-bindgen & wasm-bindgen-cli. ↩ī¸Ž

  2. Since cargo currently doesn't support target-specific dependencies, see also the recently started RFC 3374. ↩ī¸Ž

  3. The clippy crate uses a failing build script to make cargo install clippy print a message about using rustup ... however I'd really rather not resort to such hacks for my own projects, especially because this could also result in some user confusion (e.g. lib.rs would display the app label for the library-only crate). ↩ī¸Ž

10 Likes

Sounds like a great idea to me.

Isn't the ideal solution here to release the binary and library in a single package? It sounds like you are avoiding this at present because of an inability to reserve some dependencies for the binary only, but there are active RFCs aimed at addressing some or all of that problem:

3 Likes

Not necessarily since you may also want independent versions, e.g. you may want to avoid having to bump the library version when updating the CLI.

Sidenote: I already linked the latter RFC in my 2nd footnote but I now realize that Discourse unfortunately collapses them by default. I think my 3rd footnote mentions something interesting, I want to repeat here for convenience:

The clippy crate uses a failing build script to make cargo install clippy print a message about using rustup ... however I'd really rather not resort to such hacks for my own projects, especially because this could also result in some user confusion (e.g. lib.rs would display the app label for the library-only crate).

Something else I have not mentioned here yet is that the new recommended-bin-crates could also be picked up by sites such as docs.rs or lib.rs to link such related crates somewhere on the page for a library. However solving the cargo install UX issue really is the primary motivation for the suggested recommended-bin-crates field.

4 Likes

Thanks for the positive feedback, I have just opened RFC 3383 for this :slight_smile:

Let me know if the wording is clear. If you have suggestions or thoughts, please share them in the GitHub PR!

1 Like

Re. wording: the sentence

Updating this field for a library crate requires you to bump its version.

Presumably refers to bumping its version to the next patch version. Without clarification, on initial read, this can sound confusing or more severe than necessary.

Also two of the drawbacks could be relativised, which might be useful information to add, I believe: The referenced crate becoming "out-of-date" (without the referencing crate also being out of date) or yanked is not a likely scenario if the referencing and referenced crate are published by the same person/group, as they could simply avoid this problem. And the requirement that updates to a manifests field requires releasing a new version is a drawback shared with many other manifest fields.

I do not believe that the behavior of cargo add is a particularly strong argument for why library crates should get the "better" name rather than binary crates. cargo add as part of cargo is still relatively new, and the naming convention you seem to observe here is presumably a lot older. There probably are better arguments, though I could not tell them off the top of my head.

2 Likes

Thank you so much, all very good points! I have tried to incorporate your feedback into the RFC :slight_smile:

Wonderful. Another small thing: “cargo-install” and “cargo-publish” should probably better be written “cargo install” and “cargo publish”.