Version solving consists in finding a set of packages and versions
that satisfy all the constraints of a given project dependencies.
In Rust, it is the package manager Cargo that takes the dependencies specified
Cargo.toml file and deduces the complete list of exact versions
of packages needed for your code to run.
That includes direct dependencies but also indirect ones,
which are dependencies of your dependencies.
Packages and versions are not restricted to code libraries though.
In fact they could refer to anything where "packages" act as a general
name for things, and "versions" describe the evolution of those things.
Such things could be office documents, laws, cooking recipes etc.
Though if you version cooking recipes, well you are a very organized person.
The most common form of versioning scheme for dependencies is called
The base idea of semantic versioning is to provide versions as triplets
of the form
Major.Minor.Patch such as
The "semantic" term comes from the meaning of each part of the versioning system.
Publishing a new patch version, for example from
no interface has changed in the provided API of the library.
That may be the case for documentation changes,
or internal performance improvements for example.
Increasing the minor number, for example from
means that things have been added to the API,
but nothing was changed in the pre-existing API provided by the library.
Finally, increasing the major number, such as from
means that some parts of the API have changed
and thus may be incompatible with how we are currently using the previous version.
In Rust, packages are called crates and use semantic versioning.
In fact, if you specify a dependency of the form
package = "2.4.1",
cargo will interpret that as the version constraint
2.4.1 <= v < 3.0.0 for that package.
It does so based on the fact that any version in that range should not break
our code according to the rules of semantic versioning.
For more information on dependencies specifications in
you can read the Cargo reference book.
Some people think that the granularity of semantic versioning is too broad in the case of major version changes. Instead, versions should never be breaking, but use new namespaces for things that change. It brings the same benefits in the large, that what choosing immutability as default brings in the small. For more information on this point of view, I highly recommend "Spec-ulation" by Rich Hickey, creator of the Clojure programming language.