7. The 1.88 MSRV is inherited, not ours
- Status: Accepted
- Date: 2026-08-21
Context
Fleet policy is that a published library keeps a low, CI-verified rust-version
as a promise to downstream consumers. This repository was written declaring
1.85, matching its sibling container crates.
That declaration was false, and the check that caught it is worth recording
because a declared rust-version is a claim — cargo refuses on the
declaration, before compiling a line, so nothing about writing 1.85 tests it:
$ cargo +1.85 check --workspace --all-features
error: rustc 1.85.1 is not supported by the following packages:
plist@1.10.0 requires rustc 1.88.0
time@0.3.55 requires rustc 1.88.0
time-core@0.1.9 requires rustc 1.88.0
Separating our constraint from the inherited one
"Fails at 1.85" is not a result. What refused, and was it our code or a dependency's declaration is the result, and they are different columns.
Measured, by pinning the graph down and re-running the same command:
$ cargo update -p plist --precise 1.7.4
$ cargo update -p time --precise 0.3.41
$ cargo +1.85 check --workspace --all-features
Finished `dev` profile [unoptimized + debuginfo] target(s)
| Floor | |
|---|---|
ios-backup-core + ios-backup-forensic own source |
≤ 1.85 (compiles green) |
| As resolved with current requirements | 1.88, from plist via time |
So nothing in this crate's own code needs 1.88. The number is a shadow cast by a dependency, and it will move when that dependency moves.
Decision
Declare rust-version = "1.88" — the floor the crate's dependency graph actually
imposes — and record here that our own floor is lower.
Why not keep 1.85
Pinning the lockfile does not fix it. A library's Cargo.lock does not
travel to consumers. A downstream crate resolving plist = "1" gets 1.10 and
1.88 no matter what we lock, so a 1.85 declaration would be a promise that fails
for everyone except us.
Of the two MSRV defects the fleet warns about, this is the worse one. An overstated floor is merely conservative; a floor the crate's own graph cannot satisfy is simply false, and it fails outward.
Constraining the requirement — plist = ">=1.7, <1.8" — would hold 1.85, at
the cost of pinning a dependency permanently behind, fighting Renovate's
rangeStrategy: "bump", and turning the freshness gate permanently red. Trading
a true statement about compatibility for a stale dependency tree is the wrong
trade for a crate that parses untrusted input.
Consequences
- Consumers on 1.85–1.87 cannot use this crate. That is a real cost, and it is the truth rather than a surprise at build time.
- Lower this when the constraint lifts. Two routes, either of which makes 1.85 honest again with no change to our source:
plist(ortime) lowers its own floor;- we replace
plist. TheDatefield is the only thing pullingtimein, and a first-party bplist reader — or a fleet one, ifblob-decoderever grows a public parser — would drop the constraint entirely. - The MSRV CI job runs
cargo teston the declared floor, not a bare build: a job that only compiles does not prove the crate works on the floor it promises.