diff --git a/src/doc/src/SUMMARY.md b/src/doc/src/SUMMARY.md
index 5c531b7f1d8..52fa6f7af02 100644
--- a/src/doc/src/SUMMARY.md
+++ b/src/doc/src/SUMMARY.md
@@ -26,7 +26,7 @@
* [Specifying Dependencies](reference/specifying-dependencies.md)
* [Overriding Dependencies](reference/overriding-dependencies.md)
* [Source Replacement](reference/source-replacement.md)
- * [Dependency Resolution](reference/resolver.md)
+ * [Dependency Resolution](reference/resolver.md)
* [Features](reference/features.md)
* [Features Examples](reference/features-examples.md)
* [Profiles](reference/profiles.md)
diff --git a/src/doc/src/reference/manifest.md b/src/doc/src/reference/manifest.md
index 0b95bfd3f87..95de8286e4f 100644
--- a/src/doc/src/reference/manifest.md
+++ b/src/doc/src/reference/manifest.md
@@ -92,28 +92,30 @@ a keyword. [crates.io] imposes even more restrictions, such as:
### The `version` field
-Cargo bakes in the concept of [Semantic
-Versioning](https://semver.org/), so make sure you follow some basic rules:
-
-* Before you reach 1.0.0, anything goes, but if you make breaking changes,
- increment the minor version. In Rust, breaking changes include adding fields to
- structs or variants to enums.
-* After 1.0.0, only make breaking changes when you increment the major version.
- Don’t break the build.
-* After 1.0.0, don’t add any new public API (no new `pub` anything) in patch-level
- versions. Always increment the minor version if you add any new `pub` structs,
- traits, fields, types, functions, methods or anything else.
-* Use version numbers with three numeric parts such as 1.0.0 rather than 1.0.
+The `version` field is formatted according to the [SemVer] specification:
+Versions must have three numeric parts,
+the major version, the minor version, and the patch version.
+
+A pre-release part can be added after a dash such as `1.0.0-alpha`.
+The pre-release part may be separated with periods to distinguish separate
+components. Numeric components will use numeric comparison while
+everything else will be compared lexicographically.
+For example, `1.0.0-alpha.11` is higher than `1.0.0-alpha.4`.
+
+A metadata part can be added after a plus, such as `1.0.0+21AF26D3`.
+This is for informational purposes only and is generally ignored by Cargo.
+
+Cargo bakes in the concept of [Semantic Versioning](https://semver.org/),
+so versions are considered considered [compatible](semver.md) if their left-most non-zero major/minor/patch component is the same.
See the [Resolver] chapter for more information on how Cargo uses versions to
-resolve dependencies, and for guidelines on setting your own version. See the
-[SemVer compatibility] chapter for more details on exactly what constitutes a
-breaking change.
+resolve dependencies.
This field is optional and defaults to `0.0.0`. The field is required for publishing packages.
> **MSRV:** Before 1.75, this field was required
+[SemVer]: https://semver.org
[Resolver]: resolver.md
[SemVer compatibility]: semver.md
diff --git a/src/doc/src/reference/resolver.md b/src/doc/src/reference/resolver.md
index 0cb3ae5554e..21b66898b45 100644
--- a/src/doc/src/reference/resolver.md
+++ b/src/doc/src/reference/resolver.md
@@ -5,99 +5,125 @@ use based on the version requirements specified in each package. This process
is called "dependency resolution" and is performed by the "resolver". The
result of the resolution is stored in the `Cargo.lock` file which "locks" the
dependencies to specific versions, and keeps them fixed over time.
-
-The resolver attempts to unify common dependencies while considering possibly
-conflicting requirements. It turns out, however, that in many cases there is no
-single "best" dependency resolution, and so the resolver must use heuristics to
-choose a preferred solution. The sections below provide some details on how
-requirements are handled, and how to work with the resolver.
-
-See the chapter [Specifying Dependencies] for more details about how
-dependency requirements are specified.
-
The [`cargo tree`] command can be used to visualize the result of the
resolver.
-[Specifying Dependencies]: specifying-dependencies.md
+[dependency specifications]: specifying-dependencies.md
+[dependency specification]: specifying-dependencies.md
[`cargo tree`]: ../commands/cargo-tree.md
-## SemVer compatibility
-
-Cargo uses [SemVer] for specifying version numbers. This establishes a common
-convention for what is compatible between different versions of a package. See
-the [SemVer Compatibility] chapter for guidance on what is considered a
-"compatible" change. This notion of "compatibility" is important because Cargo
-assumes it should be safe to update a dependency within a compatibility range
-without breaking the build.
-
-Versions are considered compatible if their left-most non-zero
-major/minor/patch component is the same. For example, `1.0.3` and `1.1.0` are
-considered compatible, and thus it should be safe to update from the older
-release to the newer one. However, an update from `1.1.0` to `2.0.0` would not
-be allowed to be made automatically. This convention also applies to versions
-with leading zeros. For example, `0.1.0` and `0.1.2` are compatible, but
-`0.1.0` and `0.2.0` are not. Similarly, `0.0.1` and `0.0.2` are not
-compatible.
-
-As a quick refresher, the
-[*version requirement* syntax][Specifying Dependencies] Cargo uses for
-dependencies is:
-
-Requirement | Example | Equivalence | Description
-------------|---------|-------------|-------------
-Caret | `1.2.3` or `^1.2.3` | >=1.2.3, <2.0.0
| Any SemVer-compatible version of at least the given value.
-Tilde | `~1.2` | >=1.2.0, <1.3.0
| Minimum version, with restricted compatibility range.
-Wildcard | `1.*` | >=1.0.0, <2.0.0
| Any version in the `*` position.
-Equals | `=1.2.3` | =1.2.3
| Exactly the specified version only.
-Comparison | `>1.1` | >=1.2.0
| Naive numeric comparison of specified digits.
-Compound | >=1.2, <1.5
| >=1.2.0, <1.5.0
| Multiple requirements that must be simultaneously satisfied.
-
-When multiple packages specify a dependency for a common package, the resolver
-attempts to ensure that they use the same version of that common package, as
-long as they are within a SemVer compatibility range. It also attempts to use
-the greatest version currently available within that compatibility range. For
-example, if there are two packages in the resolve graph with the following
-requirements:
+## Constraints and Heuristics
+
+In many cases there is no single "best" dependency resolution.
+The resolver operates under various constraints and heuristics to find a generally applicable resolution.
+To understand how these interact, it is helpful to have a coarse understanding of how dependency resolution works.
+
+This pseudo-code approximates what Cargo's resolver does:
+```rust
+pub fn resolve(workspace: &[Package], policy: Policy) -> Option {
+ let dep_queue = Queue::new(workspace);
+ let resolved = ResolveGraph::new();
+ resolve_next(pkq_queue, resolved, policy)
+}
+
+fn resolve_next(dep_queue: Queue, resolved: ResolveGraph, policy: Policy) -> Option {
+ let Some(dep_spec) = policy.pick_next_dep(dep_queue) else {
+ // Done
+ return Some(resolved);
+ };
+
+ if let Some(resolved) = policy.try_unify_version(dep_spec, resolved.clone()) {
+ return Some(resolved);
+ }
+
+ let dep_versions = dep_spec.lookup_versions()?;
+ let mut dep_versions = policy.filter_versions(dep_spec, dep_versions);
+ while let Some(dep_version) = policy.pick_next_version(&mut dep_versions) {
+ if policy.needs_version_unification(dep_version, &resolved) {
+ continue;
+ }
+
+ let mut dep_queue = dep_queue.clone();
+ dep_queue.enqueue(dep_version.dependencies);
+ let mut resolved = resolved.clone();
+ resolved.register(dep_version);
+ if let Some(resolved) = resolve_next(dep_queue, resolved) {
+ return Some(resolved);
+ }
+ }
+
+ // No valid solution found, backtrack and `pick_next_version`
+ None
+}
+```
+Key steps:
+- Walking dependencies (`pick_next_dep`):
+ The order dependencies are walked can affect
+ how related version requirements for the same dependency get resolved, see unifying versions,
+ and how much the resolver backtracks, affecting resolver performance,
+- Unifying versions (`try_unify_version`, `needs_version_unification`):
+ Cargo reuses versions where possible to reduce build times and allow types from common dependencies to be passed between APIs.
+ If multiple versions would have been unified if it wasn't for conflicts in their [dependency specifications], Cargo will backtrack, erroring if no solution is found, rather than selecting multiple versions.
+ A [dependency specification] or Cargo may decide that a version is undesirable,
+ preferring to backtrack or error rather than use it.
+- Preferring versions (`pick_next_version`):
+ Cargo may decide that it should prefer a specific version,
+ falling back to the next version when backtracking.
+
+### Version numbers
+
+Cargo prefers the highest version currently available.
+
+For example, if you had a package in the resolve graph with:
```toml
-# Package A
[dependencies]
-bitflags = "1.0"
+bitflags = "*"
+```
+If at the time the `Cargo.lock` file is generated, the greatest version of
+`bitflags` is `1.2.1`, then the package will use `1.2.1`.
-# Package B
+### Version requirements
+
+Package specify what versions they support, rejecting all others, through
+[version requirements].
+
+For example, if you had a package in the resolve graph with:
+```toml
[dependencies]
-bitflags = "1.1"
+bitflags = "1.0" # meaning `>=1.0.0,<2.0.0`
```
-
If at the time the `Cargo.lock` file is generated, the greatest version of
-`bitflags` is `1.2.1`, then both packages will use `1.2.1` because it is the
+`bitflags` is `1.2.1`, then the package will use `1.2.1` because it is the
greatest within the compatibility range. If `2.0.0` is published, it will
still use `1.2.1` because `2.0.0` is considered incompatible.
-If multiple packages have a common dependency with semver-incompatible
-versions, then Cargo will allow this, but will build two separate copies of
-the dependency. For example:
+[version requirements]: specifying-dependencies.md#version-requirement-syntax
+
+### SemVer compatibility
+
+Cargo assumes packages follow [SemVer] and will unify dependency versions if they are
+[SemVer] compatible according to the [Caret version requirements].
+If two compatible versions cannot be unified because of conflicting version requirements,
+Cargo will error.
+See the [SemVer Compatibility] chapter for guidance on what is considered a
+"compatible" change.
+
+Examples:
+
+The following two packages will have their dependencies on `bitflags` unified because any version picked will be compatible with each other.
```toml
# Package A
[dependencies]
-rand = "0.7"
+bitflags = "1.0" # meaning `>=1.0.0,<2.0.0`
# Package B
[dependencies]
-rand = "0.6"
+bitflags = "1.1" # meaning `>=1.1.0,<2.0.0`
```
-The above will result in Package A using the greatest `0.7` release (`0.7.3`
-at the time of this writing) and Package B will use the greatest `0.6` release
-(`0.6.5` for example). This can lead to potential problems, see the
-[Version-incompatibility hazards] section for more details.
-
-Multiple versions within the same compatibility range are not allowed and will
-result in a resolver error if it is constrained to two different versions
-within a compatibility range. For example, if there are two packages in the
-resolve graph with the following requirements:
-
+The following packages will error because the version requirements conflict, selecting two distinct compatible versions.
```toml
# Package A
[dependencies]
@@ -108,14 +134,39 @@ log = "=0.4.11"
log = "=0.4.8"
```
-The above will fail because it is not allowed to have two separate copies of
-the `0.4` release of the `log` package.
+The following two packages will not have their dependencies on `rand` unified because only incompatible versions are available for each.
+Instead, two different versions (e.g. 0.6.5 and 0.7.3) will be resolved and built.
+This can lead to potential problems, see the [Version-incompatibility hazards] section for more details.
+```toml
+# Package A
+[dependencies]
+rand = "0.7" # meaning `>=0.7.0,<0.8.0`
+
+# Package B
+[dependencies]
+rand = "0.6" # meaning `>=0.6.0,<0.7.0`
+```
+
+Generally, the following two packages will not have their dependencies unified because incompatible versions are available that satisfy the version requirements:
+Instead, two different versions (e.g. 0.6.5 and 0.7.3) will be resolved and built.
+The application of other constraints or heuristics may cause these to be unified,
+picking one version (e.g. 0.6.5).
+```toml
+# Package A
+[dependencies]
+rand = ">=0.6,<0.8.0"
+
+# Package B
+[dependencies]
+rand = "0.6" # meaning `>=0.6.0,<0.7.0`
+```
[SemVer]: https://semver.org/
[SemVer Compatibility]: semver.md
+[Caret version requirements]: specifying-dependencies.md#default-requirements
[Version-incompatibility hazards]: #version-incompatibility-hazards
-### Version-incompatibility hazards
+#### Version-incompatibility hazards
When multiple versions of a crate appear in the resolve graph, this can cause
problems when types from those crates are exposed by the crates using them.
@@ -150,54 +201,6 @@ ecosystem if you publish a SemVer-incompatible version of a popular library.
[semver trick]: https://github.com/dtolnay/semver-trick
[`downcast_ref`]: ../../std/any/trait.Any.html#method.downcast_ref
-### Pre-releases
-
-SemVer has the concept of "pre-releases" with a dash in the version, such as
-`1.0.0-alpha`, or `1.0.0-beta`. Cargo will avoid automatically using
-pre-releases unless explicitly asked. For example, if `1.0.0-alpha` of package
-`foo` is published, then a requirement of `foo = "1.0"` will *not* match, and
-will return an error. The pre-release must be specified, such as `foo =
-"1.0.0-alpha"`. Similarly [`cargo install`] will avoid pre-releases unless
-explicitly asked to install one.
-
-Cargo allows "newer" pre-releases to be used automatically. For example, if
-`1.0.0-beta` is published, then a requirement `foo = "1.0.0-alpha"` will allow
-updating to the `beta` version. Note that this only works on the same release
-version, `foo = "1.0.0-alpha"` will not allow updating to `foo = "1.0.1-alpha"`
-or `foo = "1.0.1-beta"`.
-
-Cargo will also upgrade automatically to semver-compatible released versions
-from prereleases. The requirement `foo = "1.0.0-alpha"` will allow updating to
-`foo = "1.0.0"` as well as `foo = "1.2.0"`.
-
-Beware that pre-release versions can be unstable, and as such care should be
-taken when using them. Some projects may choose to publish breaking changes
-between pre-release versions. It is recommended to not use pre-release
-dependencies in a library if your library is not also a pre-release. Care
-should also be taken when updating your `Cargo.lock`, and be prepared if a
-pre-release update causes issues.
-
-The pre-release tag may be separated with periods to distinguish separate
-components. Numeric components will use numeric comparison. For example,
-`1.0.0-alpha.4` will use numeric comparison for the `4` component. That means
-that if `1.0.0-alpha.11` is published, that will be chosen as the greatest
-release. Non-numeric components are compared lexicographically.
-
-[`cargo install`]: ../commands/cargo-install.md
-
-### Version metadata
-
-SemVer has the concept of "version metadata" with a plus in the version, such
-as `1.0.0+21AF26D3`. This metadata is usually ignored, and should not be used
-in a version requirement. You should never publish multiple versions that
-differ only in the metadata tag.
-
-## Other constraints
-
-Version requirements aren't the only constraint that the resolver considers
-when selecting and unifying dependencies. The following sections cover some of
-the other constraints that can affect resolution.
-
### Features
For the purpose of generating `Cargo.lock`, the resolver builds the dependency
@@ -610,3 +613,20 @@ circumstances:
change of your own library, for example if it exposes types from the
dependency.
+[`cargo install`]: ../commands/cargo-install.md
+
+
diff --git a/src/doc/src/reference/specifying-dependencies.md b/src/doc/src/reference/specifying-dependencies.md
index 9ef98e22a01..3df66a6e298 100644
--- a/src/doc/src/reference/specifying-dependencies.md
+++ b/src/doc/src/reference/specifying-dependencies.md
@@ -19,20 +19,23 @@ guide](../guide/index.md), we specified a dependency on the `time` crate:
time = "0.1.12"
```
-The string `"0.1.12"` is a version requirement. Although it looks like a
-specific *version* of the `time` crate, it actually specifies a *range* of
-versions and allows [SemVer] compatible updates. An update is allowed if the new
-version number does not modify the left-most non-zero number in the major, minor,
-patch grouping. In this case, if we ran `cargo update time`, cargo should
+The version string `"0.1.12"` is called a [version requirement](#version-requirement-syntax).
+It specifies a range of versions that can be selected from when [resolving dependencies](resolver.md).
+In this case, `"0.1.12"` represents the version range `>=0.1.12, <0.2.0`.
+An update is allowed if it is within that range.
+In this case, if we ran `cargo update time`, cargo should
update us to version `0.1.13` if it is the latest `0.1.z` release, but would not
-update us to `0.2.0`. If instead we had specified the version string as `1.0`,
-cargo should update to `1.1` if it is the latest `1.y` release, but not `2.0`.
-The version `0.0.x` is not considered compatible with any other version.
+update us to `0.2.0`.
-[SemVer]: https://semver.org
+## Version requirement syntax
+
+### Default requirements
-Here are some more examples of version requirements and the versions that would
-be allowed with them:
+**Default requirements** specify a minimum version with the ability to update to [SemVer] compatible versions.
+Versions are considered compatible if their left-most non-zero major/minor/patch component is the same.
+This is different from [SemVer] which considers all pre-1.0.0 packages to be incompatible.
+
+`1.2.3` is an example of a default requirement.
```notrust
1.2.3 := >=1.2.3, <2.0.0
@@ -45,18 +48,6 @@ be allowed with them:
0 := >=0.0.0, <1.0.0
```
-This compatibility convention is different from SemVer in the way it treats
-versions before 1.0.0. While SemVer says there is no compatibility before
-1.0.0, Cargo considers `0.x.y` to be compatible with `0.x.z`, where `y ≥ z`
-and `x > 0`.
-
-It is possible to further tweak the logic for selecting compatible versions
-using special operators as described in the [Version requirement syntax](#version-requirement-syntax) section.
-
-Use the default version requirement strategy, e.g. `log = "1.2.3"` where possible to maximize compatibility.
-
-## Version requirement syntax
-
### Caret requirements
**Caret requirements** are the default version requirement strategy.
@@ -121,6 +112,41 @@ Here are some examples of comparison requirements:
As shown in the examples above, multiple version requirements can be
separated with a comma, e.g., `>= 1.2, < 1.5`.
+### Pre-releases
+
+Version requirements exclude [pre-release versions](manifest.md#the-version-field), such as `1.0.0-alpha`,
+unless specifically asked for.
+For example, if `1.0.0-alpha` of package
+`foo` is published, then a requirement of `foo = "1.0"` will *not* match, and
+will return an error. The pre-release must be specified, such as `foo =
+"1.0.0-alpha"`.
+Similarly [`cargo install`] will avoid pre-releases unless
+explicitly asked to install one.
+
+Cargo allows "newer" pre-releases to be used automatically. For example, if
+`1.0.0-beta` is published, then a requirement `foo = "1.0.0-alpha"` will allow
+updating to the `beta` version. Note that this only works on the same release
+version, `foo = "1.0.0-alpha"` will not allow updating to `foo = "1.0.1-alpha"`
+or `foo = "1.0.1-beta"`.
+
+Cargo will also upgrade automatically to semver-compatible released versions
+from prereleases. The requirement `foo = "1.0.0-alpha"` will allow updating to
+`foo = "1.0.0"` as well as `foo = "1.2.0"`.
+
+Beware that pre-release versions can be unstable, and as such care should be
+taken when using them. Some projects may choose to publish breaking changes
+between pre-release versions. It is recommended to not use pre-release
+dependencies in a library if your library is not also a pre-release. Care
+should also be taken when updating your `Cargo.lock`, and be prepared if a
+pre-release update causes issues.
+
+[`cargo install`]: ../commands/cargo-install.md
+
+### Version metadata
+
+[Version metadata](manifest.md#the-version-field), such as `1.0.0+21AF26D3`,
+is ignored and should not be used in version requirements.
+
> **Recommendation:** When in doubt, use the default version requirement operator.
>
> In rare circumstances, a package with a "public dependency"
@@ -622,6 +648,7 @@ rand = { workspace = true, optional = true }
```
+[SemVer]: https://semver.org
[crates.io]: https://crates.io/
[dev-dependencies]: #development-dependencies
[workspace.dependencies]: workspaces.md#the-dependencies-table