We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Modules, Visibility, Testing and Docs step 15 of 22
Version requirements: caret, tilde, wildcard, pre-releases
“Why did cargo update break my build” and “why is 0.x special” are both
answered entirely by one table. It is also perfectly shaped for a problem:
string in, bool out, and a published specification to check yourself against.
pub fn matches(req: &str, version: &str) -> bool
pub fn select(req: &str, available: Vec<String>) -> Option<String>
Caret — the default nobody types
foo = "1.2.3" in a manifest means ^1.2.3. The caret says: anything that
should not break me, which Cargo defines as “does not change the leftmost
non-zero field”.
| requirement | range |
|---|---|
^1.2.3 |
>=1.2.3, <2.0.0 |
^1.2 |
>=1.2.0, <2.0.0 |
^1 |
>=1.0.0, <2.0.0 |
^0.2.3 |
>=0.2.3, **<0.3.0** |
^0.2 |
>=0.2.0, <0.3.0 |
^0.0.3 |
>=0.0.3, **<0.0.4** |
^0.0 |
>=0.0.0, <0.1.0 |
^0 |
>=0.0.0, <1.0.0 |
The 0.x rows are where nearly everyone gets it wrong, and they explain the
churn in the pre-1.0 ecosystem: below 1.0.0 the minor field is treated as the
breaking one, so 0.2 to 0.3 is a major bump in everything but name, and
every downstream manifest has to be edited by hand.
Tilde — by component count
~ freezes everything to the left of the last field you actually wrote:
| requirement | range |
|---|---|
~1.2.3 |
>=1.2.3, <1.3.0 |
~1.2 |
>=1.2.0, <1.3.0 |
~1 |
>=1.0.0, **<2.0.0** |
~1 is the same as ^1, because you did not write a minor field for it to
freeze. How many components you typed is load-bearing.
Wildcards and operators
* matches anything (crates.io rejects a bare * in a published manifest —
having no bound at all is not a dependency specification). 1.* is >=1.0.0, <2.0.0; 1.2.* is >=1.2.0, <1.3.0. Note that 1.2.* is not ^1.2.
>=, >, <, <= and = do what they look like, with one wrinkle: applied
to a partial version they operate on the whole band. =1.2 is >=1.2.0, <1.3.0; >1.2 means “past all of 1.2.x”, i.e. >=1.3.0.
Comma-joined comparators are an AND, not an OR. >=1.2, <1.5 is one
requirement with two constraints.
Pre-releases, and the classic implementation bug
Two rules, both worth internalising.
Ordering. A pre-release sorts below the release with the same numbers:
1.0.0-alpha < 1.0.0. Within a tag, fields are compared one at a time, and a
field made only of digits is compared numerically:
1.0.0-alpha.2 < 1.0.0-alpha.11
Compare those as strings and you get the wrong answer, which is the single most common bug in hand-rolled semver code.
Admission. A pre-release only satisfies a requirement when some comparator
in that requirement names a pre-release on the same major.minor.patch. So
^1.0 never resolves to 1.2.0-beta.1, and you have to opt in explicitly with
something like >=1.0.0-alpha.2, <1.0.0. This is why pre-release versions do
not ambush you and why depending on one is deliberately awkward.
Contract
-
A
versionis always a completemajor.minor.patch, optionally with-pre.release.tag. Build metadata after+is dropped: it takes no part in ordering or matching. -
A requirement is one or more comma-separated comparators. Anything you cannot
parse means
matchesreturnsfalse. -
selectreturns the highest matching version, not the first.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.