research!rsc

Thoughts and links about programming, by

Cargo selects the latest allowed version of a new dependency and its own new dependencies, when adding them to the lock file.

Cargo also downloads code automatically during a build.

If you believe both those facts, you can stop reading now.

People are frequently surprised when I mention one or the other of these facts. Here is a shell transcript, recorded February 18, 2018.

Update: To clarify a few points. My point here is that if the dep is not in the lock file then when cargo adds it, and it has some choice in the matter, it chooses the latest version. In the example below, I wrote a dep on toml 0.4.1 (implicitly 0.4.1 or later), and cargo had to choose between 0.4.1, 0.4.2, 0.4.3, 0.4.4, and 0.4.5. It chose 0.4.5. (In the same situation, vgo will choose 0.4.1. If you want something newer, you have to ask for it.)

Furthermore, this applies to dependencies too. toml 0.4.1 depends on serde 1.0 (implicitly 1.0 or later), and cargo chooses 1.0.27, the newest one. In the same situation, in the absence of any other constraints, vgo will choose the exact version the toml author was using to develop toml. That is the one most likely to behave the way toml expects. There will be more about this in the Minimal Version Selection post, which will be linked from the series index once it is posted (Feb 21).

$ cargo
bash: cargo: command not found
$ curl https://sh.rustup.rs -sSf | sh
...
Rust is installed now. Great!
...
$ export PATH=$HOME/.cargo/bin:$PATH
$ rustup update
info: syncing channel updates for 'stable-x86_64-apple-darwin'
info: checking for self-updates

  stable-x86_64-apple-darwin unchanged - rustc 1.24.0 (4d90ac38c 2018-02-12)

$ cargo version
cargo 0.25.0 (8c93e0895 2018-02-01)
$ rustc --version
rustc 1.24.0 (4d90ac38c 2018-02-12)
$ mkdir hellorust
$ cd hellorust
$ mkdir src
$ cat >src/main.rs <<EOF
fn main() {
	println!("Hello, world!");
}
EOF
$

Now let's add dependencies on toml 0.4.1 and serde 1.0.4:

$ cat >Cargo.toml <<EOF
[package]
name = "hello_world"
version = "0.0.1"

[dependencies]
toml = "0.4.1"
serde = "1.0.4"
EOF
$ cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
 Downloading toml v0.4.5
 Downloading serde v1.0.27
   Compiling serde v1.0.27
   Compiling toml v0.4.5
   Compiling hello_world v0.0.1 (file:///Users/rsc/hellorust)
    Finished dev [unoptimized + debuginfo] target(s) in 9.58 secs
$ cargo build -v
       Fresh serde v1.0.27
       Fresh toml v0.4.5
       Fresh hello_world v0.0.1 (file:///Users/rsc/hellorust)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
$

Note that Cargo downloaded the dependencies automatically as part of cargo build. I did not run cargo update.

Note also that Cargo has decided on toml 0.4.5 and serde 1.0.27, both newer than requested. I emphasize that this is working as designed. I am not claiming it is a bug, just that some people don't believe me when I tell them.

Also, the behavior applies to unrequested dependencies too. If we start over without serde in the manifest, toml requests serde 1.0 and gets 1.0.27 once again.

$ cat >Cargo.toml <<EOF
[package]
name = "hello_world"
version = "0.0.1"

[dependencies]
toml = "0.4.1"
EOF
$ rm Cargo.lock
$ cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling hello_world v0.0.1 (file:///Users/rsc/hellorust)
    Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
$ cargo build -v
       Fresh serde v1.0.27
       Fresh toml v0.4.5
       Fresh hello_world v0.0.1 (file:///Users/rsc/hellorust)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
$

Also, both the downloading and latest version choosing happen when the lock file already exists too, for dependencies not yet in the lock file:

$ echo 'uuid = "0.5.0"' >>Cargo.toml
$ cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
 Downloading uuid v0.5.1
   Compiling uuid v0.5.1
   Compiling hello_world v0.0.1 (file:///Users/rsc/hellorust)
    Finished dev [unoptimized + debuginfo] target(s) in 0.72 secs
$ cargo build -v
       Fresh uuid v0.5.1
       Fresh serde v1.0.27
       Fresh toml v0.4.5
       Fresh hello_world v0.0.1 (file:///Users/rsc/hellorust)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
$

Here I asked for uuid 0.5.0 and got 0.5.1. Again, I am not claiming there is a bug. Cargo is working as designed.