On Mon, Jan 26, 2026, at 9:07 PM, Jonas Smedegaard wrote: > Hi Fabian, > > Quoting Fabian Grünbichler (2026-01-26 19:42:12) >> CC-ing the Rust team discussion list >> >> On Mon, Jan 26, 2026, at 8:17 AM, Jonas Smedegaard wrote: >> > Quoting Daniel Kahn Gillmor (2026-01-26 05:23:44) >> >> Hi Jonas-- >> >> >> >> On Sun 2026-01-25 09:42:21 +0100, Jonas Smedegaard wrote: >> >> > Thanks, but I think you are mistaken: In my experience, "<= 0.6" is >> >> > equivalent to "<= 0.6.*" (not "<= 0.6.0"). >> >> >> >> Interesting, thanks for pushing back and making me reconsider this! >> >> >> >> i read the cargo specification for version requirements: >> >> >> >> >> >> https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#version-requirement-syntax >> >> >> >> and it appears to be ambiguous about what an explicit comparator means >> >> if the patch level is unspecified. >> >> >> >> > If I was mistaken, then I believe that e.g. oxigraph, which for some >> >> > time has carried this line: >> >> > >> >> > quick-xml = ">= 0.37, <= 0.38 >> >> > >> >> > would FTBFS with librust-quick-xml-dev 0.38.4-1, and that works fine. >> >> >> >> If you have evidence that this works, that's good enough for me. >> > >> > Speaking of versioning of Rust crates, I thought of another related one >> > just after hitting send yesterday: >> > >> > I have noticed a common pattern in the Rust team of including features >> > when declaring version constraints, like this: >> > >> > librust-rusqlite+blob-dev (<< 0.38-~~) >> > librust-rusqlite+blob-dev (>= 0.29-~~) >> > >> > (the example is from librust-sequoia-cert-store-dev that you might be >> > directly familiar with). >> > >> > That pattern has two weaknesses: It is escapable and it is vague. >> > >> > It is escapable because it permits a too old *and* and too new version, >> > e.g. in above example it does not rule out Debian packages versioned >> > 0.28-1 and 0.39-1. >> >> it does. there is only one source package providing the unversioned >> package librust-sqlite-dev (and the corresponding +feature variants), >> rust-sqlite. if that package's version is within the version range, it can >> satisfy both dependencies. if it is not, it can only satisfy one of them. two >> versions of the same package are not co-installable. > > That sounds like a Rust team practice, not a dpkg issue in Debian in > general, which I was reflecting on above. No problem, I don't mind us > switching to talking about the Rust team conventions, more narrowly. > > Ok, so Rust-team crate packages provide unversioned virtual packages > only for unversioned source packages. Which means that the example > above would ignore a newly introduced version-branch e.g. of > rust-rusqlite-0.29 (same way as e.g. rust-darling-0.14 is done).
Yes, my response was of course fully in the context of Debian Rust (team) packages. For the avoidance of doubt, I'll spell out the policy ;) rust-foobar will build librust-foobar-dev which will provide: - librust-foobar-X-dev - librust-foobar-X.Y-dev - librust-foobar-X.Y.Z-dev as well as (for every feature, `foobaz` being an example here): - librust-foobar+foobaz-dev - librust-foobar-X+foobaz-dev - librust-foobar-X.Y+foobaz-dev - librust-foobar-X.Y.Z+foobaz-dev For some packages there might exist a separate binary package librust-foobar+something-dev, which again provides all those versioned variants for +something, as well as an unversionend and all the versioned variants for other features that are grouped together with `something`. Semver-suffixed packages like rust-foobar-0.Y or rust-foobar-X (where X != 0) will build the corresponding librust-foobar-0.Y-dev / librust-foobar-X.dev, which will provide - librust-foobar-X-dev (if not already the binary package name) - librust-foobar-X.Y-dev (if not already the binary package name) - librust-foobar-X.Y.Z-dev as well as (for every feature, `foobaz` being an example here): - librust-foobar-X+foobaz-dev - librust-foobar-X.Y+foobaz-dev - librust-foobar-X.Y.Z+foobaz-dev The same "there might be other +something binary packages" applies here as well. So in the case of rusqlite, at the moment, since it is still at version 0.x, we will have rust-sqlite (the latest packaged version) and possibly rust-sqlite-0.Y for various values of Y. A version range like rusqlite = ">= 0.29, < 0.39" would thus cross semver boundaries (by virtue of being at 0.Y, where every bump of Y is semver breaking) can only be encoded as librust-rusqlite+default-dev (<< 0.38.0-~~), librust-rusqlite+default-dev (>= 0.29-~~) which can never be satisfied by packages built from rust-sqlite-0.Y, but only by those built by rust-sqlite itself. by virtue of only a single version of that package being installable, that single version must either satisfy both version constraints, or none or only one, in which case the (build-)dependencies would not be satisfiable. > If we wanted the cross-boundary dependency to take alternative packages > into account, then dependencies would need to be adjusted to instead be > zero-bound (looking at how rust-darling-0.14 and rust-darling both > provide zero-versioned virtual packages), but that would introduce the > issue of "leaky" dependencies. The version range encoding is special for that reason - we only emit ranges as dependencies that are satisfiable by: - only the packages built by rust-foobar (if range crosses semver boundaries) - the packages built by rust-foobar-X(.Y) and rust-foobar, while both are still at the same semver version (during the transition itself, if the range doesn't cross semver boundaries) - the packages built by rust-foobar-X(.Y), if the range doesn't cross semver boundaries and the transition is done and rust-foobar has moved on >> if the version range does not cross semver boundaries, then the range is not >> encoded using the unversioned package, but using the "semver-dominant" >> component. e.g., a range like >= 2.1, < 2.4 would be encoded as >> librust-foobar-2-dev (>= 2.1-~~), librust-foobar-2-dev (<< 2.4), which might >> be >> satisfied by librust-foobar-2-dev built from rust-foobar-2 or by >> librust-foobar-dev built from rust-foobar. in practice this is not an issue >> either, since introducing rust-foobar-2 coincides with upgrading rust-foobar >> to >> 3 (or higher). > > Makes sense (except for the "either", see above). I hope the more detailed explanation above makes sense :) >> this encoding was carefully chosen because the old way we encoded such >> version >> ranges (using alternate build-/dependencies) was broken (indeterministic) and >> not policy compliant. the rust team book still contains the old encoding, I >> will update this ASAP. > > I am not familiar with that older style. Thanks for checking and fixing > documentation related to that. the older style was: foobar = ">= X, < Z" => librust-foobar-X-dev | librust-foobar-Y-dev | .. | librust-foobar-Z-dev possibly with version constraints at the X and Z packages. this runs afoul of the "buildds only pick first option in alternate dependencies" principle. >> > I would, for dependencies that cross semver stability boundaries, stop >> > care about feature and stop care about the least concerning of either >> > upper or lower bounds, e.g. for the above example (where we factually >> > know that lower bounds is not an issue) I would instead declare this: >> > >> > librust-rusqlite-dev (<< 0.38-~~) > > Oh, I forgot to refine after copying: I see no need for the trailing > "lesser-than-any-debian-revision" rune, but would simplify to this: > > librust-rusqlite-dev (<< 0.38) > FWIW, I plan to drop those pesky -~~ in debcargo, they make no sense AFAICT: https://salsa.debian.org/rust-team/debcargo/-/issues/53 https://salsa.debian.org/rust-team/debcargo/-/commit/bdecd4e11c368c5768998aa7c5c401396e827bb3 >> we (as in, the Rust team) have discussed dropping both the full 3-component >> version provides/depends (those are rarely needed in the first place[0,1]) >> and/or reducing the amount of feature related provides/depends. it requires >> careful thought to not miss edge cases. > > Thanks for considering. Yes, I am aware it is not simple to do the > change. > >> > That is half the amount of nodes to compute in the dependency graph, >> > and a reduction in potentials for complexity due to reduces possibility >> > types of edges. And likely more important, it reduces the size of the >> > packaging metadata. >> >> in general, reducing dependencies like that should work. of course, it >> doesn't >> account for valid package split along feature lines, or the fact that the >> unversioned package is only provided by the non-semver-suffixed source >> package. >> so if you reduce dependencies in that fashion, it will likely work - but you >> might be asked to adapt if a package is split or semver-suffixed later on, as >> your package might FTFBS without those adaptations. > > Makes sense. > > Thanks for your comments, > > - Jonas > > -- > * Jonas Smedegaard - idealist & Internet-arkitekt > * Tlf.: +45 40843136 Website: http://dr.jones.dk/ > * Sponsorship: https://ko-fi.com/drjones > > [x] quote me freely [ ] ask before reusing [ ] keep private

