I am new to Rust and is trying to understand the Cargo thing. I read in their FAQ about "why do binaries have Cargo.lock in version control, but not libraries?" but do not understand what this means.
"Users dependent on the library will not inspect the library’s Cargo.lock (even if it exists). This is precisely because a library should not be deterministically recompiled for all users of the library.
If a library ends up being used transitively by several dependencies, it’s likely that just a single copy of the library is desired (based on semver compatibility). If Cargo used all of the dependencies' Cargo.lock files, then multiple copies of the library could be used, and perhaps even a version conflict."
Appreciate if anyone can explain this. Thanks.
Say we have the following crates:
string-tools
: this is some kind of commonly used library exporting a FastString
struct that has faster implementations of some commonly needed functions. This library has two versions: 1.0.1 and 1.0.2. Version 1.0.2 was recently released.database
: a library to interface with your favorite database. It needs to do some string processing, and so it uses the string-tools
library. One of the public methods in database
has a signature like this:
fn get_username(id: u64) -> string_tools::FastString
The library has not had a chance or need to update to version 1.0.2 of string-tools
- maybe it's unaffected by any of the bugs that were fixed in the patch. Consequently, it's Cargo.lock pins the version of string-tools
to 1.0.1.client
: this library is for interacting with the client. It also depends on string-tools
, and has a method like this:
fn show_name(name: string_tools::FastString)
This library is using the most recent version of string-tools
, version 1.0.2. That is also the version in its Cargo.lock.You would like to write a website that makes use of the database
and client
libraries. When you build your project, Cargo compiles each library's dependencies with the versions that are specified in the Cargo.lock. This means that database
uses version 1.0.1 of string-tools
and client
uses version 1.0.2. But now imagine that you've written some code like this:
client::show_name(database::get_username(id));
This should compile. After all, the get_username
function returns a string_tools::FastString
, which is accepted by the show_name
function. But it doesn't! That's because the FastString
returned by get_username
comes from version 1.0.1 of string_tools
, while show_name
wants a FastString
from version 1.0.2! This can't work; after all, when the patch to string-tools
for version 1.0.2 was written, it's possible that the author added an additional field to the type. There's nothing reasonable for the compiler to do.
This kind of issue is avoided by having Cargo ignore the Cargo.lock file on libraries. Instead what it does is compile both database
and client
against the 1.0.2 version of string_tools
. This is correct because 1.0.1 and 1.0.2 are "semver compatible versions." That means it is ok to change out one for the other, things must still compile. Now you no longer get a compiler error, because the types that the one function returns and the other function accepts are no longer different.