rfcs icon indicating copy to clipboard operation
rfcs copied to clipboard

RFC 3283: Backward compatible default features

Open tbu- opened this issue 2 years ago • 7 comments

Rendered

tbu- avatar Jun 23 '22 14:06 tbu-

The problem that this RFC solved can already be solved with semantic versioning, isn't it? Adding a new default-feature in a crate could be seen as a breaking change (for users having default-feature=false).

Unless I'm missing something, I feel using semantic versioning with a new major version should be mentioned in the Alternatives section at the very least.

woshilapin avatar Jul 07 '22 06:07 woshilapin

yeah but you can just go turn on the feature and you're back in business, so it's not a big deal.

Lokathor avatar Jul 07 '22 06:07 Lokathor

The problem that this RFC solved can already be solved with semantic versioning, isn't it? Adding a new default-feature in a crate could be seen as a breaking change (for users having default-feature=false).

Semantic versioning isn't a solution to this. Semver-incompatible changes should generally be avoided, almost at all costs. This is evidenced by the standard library which also has this very problem and will definitely not consider breaking semver to introduce a new on-by-default feature.

tbu- avatar Jul 09 '22 20:07 tbu-

This is evidenced by the standard library which also has this very problem and will definitely not consider breaking semver to introduce a new on-by-default feature.

The standard library is a special case. There are a handful of core crates in the ecosystem that really must avoid semver breaking changes, but in most cases it's entirely possible to release new major versions with the only downside being a period of extra build time because of the duplicated crates until everyone updates.

Nemo157 avatar Jul 11 '22 10:07 Nemo157

Another perspective on this RFC is that it is adding another dimension to semantic versioning. We have a breaking version number at the package.version level but now we also have breaking version number package.feature-bases level.

The intended value of this feature is allowing decomposing the breaking version number into smaller pieces to offer users some extra stability as changes are made. This is somewhat similar to the case of a crate providing a subset of the API in "core" crates and us merging https://github.com/rust-lang/rfcs/pull/3243 to allow better composing of these crates that would normally be re-exported.

The question for me is whether it pulls its weight when considering

  • benefits of avoiding bumping the breaking version in package.version
  • Scope and impact of crates needing versioned "default/default-features-false`
  • users learning to deal with another "version"
  • users managing another "version"

epage avatar Jul 11 '22 12:07 epage

The standard library is a special case. There are a handful of core crates in the ecosystem that really must avoid semver breaking changes, but in most cases it's entirely possible to release new major versions with the only downside being a period of extra build time because of the duplicated crates until everyone updates.

I disagree. Every crate should strive to maintain backward compatibility unless there's some very good reason not to. Of course, it's possible to release a new major version, but it comes with the significant disadvantage that reverse-dependencies needing to upgrade, and sometimes that traits and types from major versions are incompatible without using the semver trick.

tbu- avatar Jul 11 '22 20:07 tbu-

I disagree. Every crate should strive to maintain backward compatibility unless there's some very good reason not to. Of course, it's possible to release a new major version, but it comes with the significant disadvantage that reverse-dependencies needing to upgrade, and sometimes that traits and types from major versions are incompatible without using the semver trick.

Yes, every crate should follow semver in that a breaking change bumps the major version.

Now how much crates avoid bumping major versions is dependent on scope/impact. Some crates serve as "vocabulary terms", meaning they are core to cross-crate communication, like regex, tokio, etc. These need to minimize major version bumps due to splitting the ecosystem.

However, most crates don't fall into that category get used more directly and have little interop, reducing the impact of a breaking change. nom is a great example of this. Unnecessarily burdening these crates with pressure to not bump major versions can have a major dampening effect on the Rust ecosystem from growing.

epage avatar Jul 11 '22 21:07 epage

I would just like to point out that a feature base is the same as a feature. If we required the feature list to never be empty (e.g. if no default features had to be used with at least one feature) we wouldn't have this problem.

I'm happy to admit maybe their are simpler solutions than my old RFC (which frankly I had all but forgotten about) but it would be nice to e.g. in a future edition have a simpler Cargo.toml spec.

Ericson2314 avatar Oct 10 '22 14:10 Ericson2314

We've discussed this in a few @rust-lang/cargo meetings. In particular, we're especially not in favor of the idea of having the version specified in Cargo.toml affect feature resolution, as opposed to the version actually selected by the resolver.

In general, we feel like we're favoring an approach more like https://github.com/rust-lang/cargo/issues/3126 rather than this approach. (That still needs some details worked out, though.)

@rfcbot close

joshtriplett avatar Nov 01 '22 17:11 joshtriplett

Team member @joshtriplett has proposed to close this. The next step is review by the rest of the tagged team members:

  • [ ] @Eh2406
  • [x] @ehuss
  • [x] @epage
  • [x] @joshtriplett
  • [ ] @weihanglo

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

rfcbot avatar Nov 01 '22 17:11 rfcbot

In general, we feel like we're favoring an approach more like https://github.com/rust-lang/cargo/issues/3126 rather than this approach. (That still needs some details worked out, though.)

It seems to me that the approach outlined in the linked issue will not allow people (or the standard library, for that matter) to add new features for existing functionality, because it only adds a way to specify dependencies. This means that you cannot make more parts of your library optional without it being a breaking change, because people might rely on their default-features = false build compiling.

In general, when adding a new default feature to a crate, there seem to be two cases:

  1. The feature can be used (by deselecting it) to disable already-existing functionality. In this case, existing crates not knowing about the feature should keep it enabled.
  2. The feature can be used to add new functionality. In this case, existing crates trying to disable default features and not knowing about the feature should probably have it disabled as well.

The current default-features = false enables use case 2, the linked issue would enable use case 1 if default-features = false were to go away, but this will never be the case (as I understand it). This means the linked issue will not actually enable library creators to use default features in a new way. default-features = true cannot work with use case 1 (it'll result in a compiler error) and the linked issue cannot work with use case 2 (it'll result in feature bloat).

In particular, we're especially not in favor of the idea of having the version specified in Cargo.toml affect feature resolution, as opposed to the version actually selected by the resolver.

I see. I think this makes sense. I think it also implies that not both of the use cases 1 and 2 can be satisfied at the same time.

tbu- avatar Nov 01 '22 17:11 tbu-

:bell: This is now entering its final comment period, as per the review above. :bell:

rfcbot avatar Nov 01 '22 17:11 rfcbot

We've discussed this in a few @rust-lang/cargo meetings. In particular, we're especially not in favor of the idea of having the version specified in Cargo.toml affect feature resolution, as opposed to the version actually selected by the resolver.

This comment is very confusing to me. With the proposal here in this RFC, there is no strange dependency on the version specified in the depending package's Cargo.toml. That strange dependency is a property of https://github.com/rust-lang/rfcs/pull/3146, not this MR.

The proposal in this MR is admirably simple and seems to resolve all the relevant issues quite well. It seems to me to be better than #3126 which is now apparently being suggested as the alternative.

I suspect there is some confusion about which proposals do what. I wonder if maybe the @rust-lang/cargo discussion was actually about #3146, not about this proposal. In any case, I think the stated rationale for closure doesn't make sense.

ijackson avatar Nov 10 '22 13:11 ijackson

@ijackson while the reason listed was mixed up with #3146, we have been talking about both and our general expectation for which direction to go still applies.

I can't speak for the others but I feel this proposal adds a lot of upfront complexity for people to depend on a crate when bases are being used.

epage avatar Nov 10 '22 13:11 epage

I will admit that I got the RFC discussions confused, and so was the source of the erroneous explanation. Sorry.

Being confused by what all the options are, how they interact, and what impact each one will have is not uncommon when discussing the features system. Xavier Denis and I have been discussing creating a mathematical model of the features system so as to have rigorous definitions of the proposals. If that discussion goes anywhere, this proposal should definitely be added to the list of things being modeled.

Eh2406 avatar Nov 10 '22 15:11 Eh2406

I can't speak for the others but I feel this proposal adds a lot of upfront complexity for people to depend on a crate when bases are being used.

I can see how it adds complexity for the people creating feature bases, creating a dependency for others. Can you describe why it makes it complicated for users of the dependencies? They can continue to use default features, or instead use a different feature base. Obviously only one that existed for the version they depend on, but that's already true for features, they can get added in later versions.

tbu- avatar Nov 10 '22 17:11 tbu-

The final comment period, with a disposition to close, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This is now closed.

rfcbot avatar Nov 11 '22 17:11 rfcbot

https://github.com/rust-lang/rfcs/pull/3347 Here's a 3rd attempt on this problem

Ericson2314 avatar Nov 11 '22 23:11 Ericson2314