4 ms·
And whenever this claim comes up, it's time to point to the API evolution RFC that outlines which kinds of breaking changes are acceptable or not: https://rust-
by burntsushi 4y ago
And whenever this claim comes up, it's time to point to the API evolution RFC that outlines which kinds of breaking changes are acceptable or not: https://rust-lang.github.io/rfcs/1105-api-evolution.html https://rust-lang.github.io/rfcs/1105-api-evolution.html
It's a classic communication problem. When we talk about breaking changes, we talk about what we have defined to be a breaking change. Obviously, if our definition didn't come close to approximating what a literal breaking change is in practice, then it wouldn't be a very good one. Similarly, if our definition were equivalent to a literal breaking change, then it also wouldn't be a very good one because it would prevent way too many types of additions that do not look like they should be a breaking change.
It's likely it would have been better to design the language such that there was no gap in the definition of "breaking change." But that never happened and probably never will.
As a mitigation mechanism to bridge the gap, we use a tool called crater that checks whether changes break compilation of published code. It is by no means perfect, but it helps. For example, if a change breaks code but would otherwise be acceptable API evolution, we will often not do it or otherwise proceed more carefully.
- est31 4y agoYeah and note that this RFC is older than 1.0, so very likely older than any of the code that was "broken" by the addition of a function. Also note that you have to go out of your way to get broken by such additions: you have to declare a trait, then implement that trait for a group of types that you didn't define, and then use that trait on standard library types. Only then will you come into conflict. Also, if you used a name that would never end up in the standard library, you are also safe. Last, often additions of functions are held back because of the breakage they would cause.
- IshKebab 4y agoYeah I feel like 0 breakage is just an unnecessarily strict goal. I like Typescript's approach. They just try to minimise breakage. Also probably worth noting that JavaScript adds new functions that could break existing code all the time and I haven't heard anyone ever complain about it.
- jswrenn 4y ago> Also note that you have to go out of your way to get broken by such additions: you have to declare a trait, then implement that trait for a group of types that you didn't define, and then use that trait on standard library types. There's some mild irony here, in that you're replying to burntsushi, who just released 1.0 of a library that employs this pattern: https://news.ycombinator.com/item?id=32762345 https://news.ycombinator.com/item?id=32762345 I also think you might be downplaying the ease of hitting of the problem, too. While RFC1105 declares that this sort of breakage is acceptable, Rust's maintainers are, in practice, (understandably) hesitant to break builds. The stabilization of `Iterator::intersperse` was aborted after it was discovered that doing so would break the no-less-than 319 crates that directly or transitively depend on `Itertools::intersperse`: https://github.com/rust-lang/rust/pull/89638 https://github.com/rust-lang/rust/pull/89638 Speaking as one of the maintainers of itertools: This whole situation feels kinda awful, and kinda makes me want to discourage others from using itertools. :(
- est31 4y ago> who just released 1.0 of a library that employs this pattern oh interesting, the irony lol. Indeed there are many identifiers in there that cause the danger of breakage should the standard library ever add them. > would break the no-less-than 319 crates Wow that's a lot. I guess this problem scales with the size of Rust, which is ever increasing in number of users as well as crates. As an anecdote, one of my first additions to the standard library also ran into this, but at a way smaller scale: https://github.com/rust-lang/rust/issues/41793 https://github.com/rust-lang/rust/issues/41793 Back then we didn't have the PR yet that would make nightly functions not conflict with downstream functions: only if you enabled the nightly function the conflict would exist. So it would move that breakage from the introduction to the function to its stabilization. > and kinda makes me want to discourage others from using itertools. :( What you get by this pattern is a shared namespace: shared by both itertools and std libraries. In C, every non-file-private function is part of the shared namespace, and there, libraries have evolved to prefix their function names by the name of the library. So if two libraries have a check_for_errors function, there is no conflict as they are named libfoo_check_for_errors and libbar_check_for_errors. So my suggestion would be to change itertools in that pattern. Or you might even use a shortening of the itertools name like itls, as long as nobody would ever add a function name like that to the standard library.
- School-Cotton 4y agoWhen people on HN claim that the Rust team does not break code that worked in previous language versions, they are not citing that RFC, they are using the term “breaking change” in its usual plain English meaning. I think it is not just an issue of semantics: there is actually a widespread popular belief that the Rust project guarantees no breaking changes except to fix bugs, which is simply not true. Perhaps the Rust team itself is careful not to actually claim this, in which I retract my assertion that they have done so. But it’s certainly something that is heavily implied as a selling point of the language.
- burntsushi 4y agoBoth things can be true. It can be true that we (the Rust team, I am on libs-api) are careful about what we claim while the "plain English" version of it is also true. I explained the mitigation measures we take, and it is quite good at telling us whether a change is going to break people, even if it is strictly acceptable from our API evolution policy. I'm not trying to make a just a dumb semantic point here. We really do take the literal notion of breaking change seriously too. The last time I myself experienced a breaking change was maybe 5 or 6 years ago. Neither ripgrep 0.2.0 nor 0.3.0 compile on current Rust. Although neither of the breaking changes came from library APIs.
- avgcorrection 4y agoI have seen many discussion through the years about how important semantic versioning is. Then every project seems to end up using their own definition of breaking change, which makes semantic versioning seem overhyped as a universal tool (although probably useful for each individual project because they know exactly what it entails).
- burntsushi 4y agoNot sure what relevance that has here. Semver is just a tool to manage communication among a group of decentralized humans. Communication is always hard. I personally use semver where I can, especially when I intend for others to use my code, because it is exceptionally good at signaling "this release may require some manual changes or extra work." And because it's good at that, tooling has been built up around it that makes it ever better. Of course, semver has exceptionally small bandwidth. And sometimes people can disagree about what a breaking change is (does increasing the toolchain version required to build your library constitute a "breaking change?"). And sometimes behaviors of code are so difficult to specify that breaking changes may happen unintentionally. And yes, things get overhyped. That's been happening with all sorts of things in tons of different contexts. I fail to see how it's an interesting remark in this case.
- avgcorrection 4y ago> Semver is just a tool to manage communication among a group of decentralized humans. Some people have argued that the main point is about being machine-readable. But that’s not relevant as long as the consumers are all on the same page (users of NPM probably don’t have to care that much about what definition Rust uses). > Of course, semver has exceptionally small bandwidth. Yes. Just a flag in the breaking-change case. Maybe a data exchange format would have been better if we really wanted to communicate precisely what we have changed (again, if machine-readability was ever a concern). > And sometimes people can disagree about what a breaking change is The Semver 2.0 page defines it as a change to the public API (or “the API” since if the API is not public then it doesn’t even matter). So if you define your API then you should know what it would take to “break” things. > I fail to see how it's an interesting remark in this case. Sorry to have bothered you. Again.
- benreesman 4y agoOk, so why not say: “Our core maintainers internally use a definition of ‘breaking change’ that is different than the commonly accepted definition as a useful, and we feel necessary, fiction. This is detailed in RFC such-and-such, along with our rationale for this somewhat surprising choice. The reality is that we do break working production code past 1.0, though we invest great effort in trying not to, and we believe the benefits of being able to occasionally break production code outweigh the costs in practice”. Linus doesn’t break userspace. Back when Raymond Chen’s clique at Microsoft ran the show, Windows would honor use-after-free in SimCity or whatever on a new kernel. They didn’t break working stuff. Python 3 took forever because they felt it necessary to tear out subtle but critical assumptions, e.g. around strings and UTF-8 and stuff. “We don’t break stuff.” is hard, it has serious costs to go along with it’s obvious benefits, and it’s not the only way. It’s fine if Rust is going a different way, and it might be a revolution in this is a better way, we’ll know in twenty short years! :) But trying to lawyer around it so that “Rust isn’t wrong, you’re wrong, just see Appendix F Subsection 42.6” when someone is staring at a broken build is not the Done Thing. It’s not particularly respectful to the people who bend over backwards to honor the common-law definition of “break”, and it’s a canonical example of what many hackers dislike about interactions with the Rust community. I feel very strongly there is a more productive path here.
- steveklabnik 4y agoWhile maintainers may use a very technical sense of breaking change, that doesn’t mean in the end it doesn’t correspond to a lay usage of the term. The “ "Upgrading to a new stable compiler version requires either no changes or extremely small and easy changes to my code," question I posted below is what I would argue the lay understanding of the term means, and as the survey showed, it is extremely close to zero. That you believe that the team goes “uhhh actually sorry your code isn’t broken due to subsection 2.4.f” shows that you don’t actually engage with Rust in this respect. Can you cite the Rust team breaking a bunch of user code and then responding this way when the inevitable outcry comes? I can’t think of a single time that that’s happened, though I am of course fallible. Given that you claim it happens regularly it should be easy! Literally the comment that started this sub thread is about the Rust team not making a change that would break a reasonably popular tool because it would harm users.