Closed ripytide closed 1 month ago
I know it's a tough problem. I don't expect to have 100% pass on cargo-semver-checks - breakage needs to be weighed against how likely it is, and how difficult it is to fix it.
I think it's important to have pairs of versions interoperable for a gradual upgrade. "Flag day" upgrades are difficult for users. For you the priority is in having a clean interface, but for some users it's more important to minimize maintenance costs and avoid disruptions.
Hopefully many theoretically-breaking changes won't be breaking in practice. For example, inherent .alpha()
method won't cause compilation errors. Traits are ambiguous only against other traits, not methods. As long as both return the same type, it should be good enough.
If there are some trait methods that have overlapping names but differ in their return type or arguments, we could first deprecate and rename them in v0.8 to prepare users for upgrade.
I think the stages could be:
Okay, I'm happy to do pair-wise updates using deprecation as a tool, I didn't realize we could deprecate things in a patch release, that makes things much easier.
Perhaps it would be a good idea when we're getting ready to make the v0.9
release to start with an alpha release v0.9-alpha.1
so downstream crates can experiment with the new version and report any changes they'd like to see. Alternatively we can just commit straight to v0.9
and the make any changes when going to v0.10
or v1.0
.
Prereleases of 0.9 are a good idea
I've been working on adding back all the functionality from
v0.8
tov0.9
that cannot be implemented inv0.8
due to orphan rules which is required for a non-breaking finalcargo-semver-tricks
v0.8
release to work with 0% chance of breaking changes.To do this I've added all the
v0.8
inherent functions/trait_impls (which aren't otherwise desired in the normalv0.9
library) into a separate module which is conditionally enabled with a feature, something likelegacy
.However, this got me thinking: how long do we have to keep all this unwanted functionality? A lot of which could seriously interfere with newer functionality in
v0.9
such as<P as HasAlpha>::alpha()
vsRgb::alpha() -> Rgba
which can cause naming ambiguity.This is made worse due to cargo feature unification which means even if a user of
v0.9
doesn't enable thelegacy
feature (which they should never ever do if depending on av0.9
) as long as there exists a single crate in their dependencies usingrgb v0.8
(which does enablergb v0.9
'slegacy
feature) then this feature will be enabled for every crate in the dependency tree.This could cause really annoying bugs that only show up when compiling a crate using
v0.9
in a dependency tree with another crate usingv0.8
. Such as naming collisions. This seems rather unfortunate to me and importantly less advantageous than simply doing a breaking release since at least thencargo
can use two versions ofrgb
,v0.8
andv0.9
, which means within-crate functionality won't break so easily.If we ever want to hit
v1.0
with a strong guarantee that we will never need to make breaking changes again then the best way to achieve that in my opinion is to remove all the inherent methods from the types and move them all to traits (as we have been doing). Since traits can be easily deprecated and new traits added, but inherent methods, although they can be deprecated, will always be within the namespace of a file which makes them much more damaging in the long term as their function names will be forever reserved and unavailable to be improved. Imagine a problem being found with an inherentmap()
function so amap2()
has to be created but maybe one day that needed to be improved too so amap3()
function has to be made and so on...What I'm trying to get at is that although making a breaking change would be really bad for the ecosystem and make others do a lot of work to upgrade as we've discussed in #84, I think it is the better option longterm-wise for rust as a language and as an ecosystem.
You mentioned in https://github.com/kornelski/rust-rgb/issues/83#issuecomment-2135071540 that we could use the
legacy
feature hack to provide a smooth migration fromv0.8
tov0.9
and then drop the hacks inv1.0
but I don't see how this would work as then there would be no way of doing thesemver-trick
fromv0.8
tov1.0
which makes it a breaking change anyway which is the same as doing a breaking change fromv0.8
tov0.9
but without the brief transition period which wouldn't make much of a difference to the crates which are re-exportingrgb
types since going fromv0.9
tov1.0
would become a technically breaking change just with a lot more subtlety.If you think this strategy, temporary non-breaking transition
v0.9
then breakingv1.0
, is acceptable to you but a breakingv0.8
tov0.9
is not then I am absolutely fine with that and will continue work on thelegacy
module as long as we are aware thatv1.0
will be technically breaking.