Rust 1.x seems to not always be backward compatible in practice
utcc.utoronto.ca
utcc.utoronto.ca
But why bother with that when you can write passive-aggressive blog posts that are devoid of useful information about the errors that were encountered instead.
You don't even have to be affecred by it on any of your programs to want to point it out and criticize it...
Rust does not claim perfect backwards compatibility. For instance, unstable features have no backwards compatibility promise.
I don't have to prepare for the possibility of having live tech support open whenever I update the Go binary in my projects. Rust advocates should not try to suggest this is reasonable.
If the Rust compiler developers intend to provide backwards compatibility, but accidentally make a change that breaks that, then that is absolutely 100% a bug. You are allowed to prefer that the software you use has minimal bugs, but you are deluding yourself if you think that the Go compiler (or essentially any other piece of software) is bug free.
Perhaps that makes this a Firefox bug, but given Firefox's historical relationship with Rust, that's not a good look for the Rust community either.
> In this case it looks like something in a Cargo.toml in Firefox wasn't to the taste of the newer version of Cargo. Presumably it was valid back then but not any more.
They may have also meant "a new dependency version broke", not literally that Cargo couldn't understand Cargo.toml. Need more details.
i am immediately annoyed by the author using this rhetorical flourish
> But they never made this complaint in the actual Bugzilla where someone could consider their feedback, they skipped straight to the "complain via blog post" phase. [...] My advice to the author is that perhaps they should extend a little more charity and "just ask" first, before they jump at the opportunity to complain in public - without having mentioned the issue to anyone with the ability to fix it.
Which comment links in turn to another post, for which the blogger writes,
> I would file a bug report but I cannot imagine that Fedora would accept it. They already know that this feature doesn't work; it's right there in the DKMS manpage. It's there anyway because, well, I don't know. This is the most user-hostile decision I think I've ever seen Fedora make.
but in fact the issue was fixed within a week of reporting it.
Firefox, in particular, uses the not-so-secret escape switch to unlock Rust nightly features on Rust stable. A bunch of folks on the Rust team are pretty unhappy about this (see e.g. https://github.com/rust-lang/cargo/issues/6627), but it is the current situation. So "Firefox broke after I ran rustup" is a very different complaint from "My code, on Rust stable, without the secret escape switch broke after I ran rustup," and the blame (if there is any blame at all, which is unclear without the actual error report) is on the Firefox developers, not the Rust developers.
In the general case, the Rust folks care a lot about backwards compatibility, but in turn they do that with an eye towards never having a Rust 2.0. They've taken a lot of innovative steps towards making Rust 1.x usable indefinitely, including the nightly/unstable system, the epoch system, crater, etc. A consequence of staying on Rust 1.x indefinitely is that you cannot possibly be bug-for-bug compatible with Rust 1.0 and Cargo 1.0 for all theoretically possible code (see Hyrum's Law: https://www.hyrumslaw.com/). However, if something breaks in the real world, report it, and I expect the Rust developers will be sympathetic and try to fix it. Without any indication of what broke, nobody can do anything with this post except get unactionably mad at other people, and that's pretty useless for actually getting things done.
The author is, of course, free to blog whatever he likes. But I think the HN community should stop upvoting his clickbait posts.
Sadly there is some cult around the whole community... although I find the core developers to be pretty great.
« We reserve the right to fix compiler bugs, patch safety holes, and change type inference in ways that may occasionally require new type annotations. »
While Rust's handling of backcompat isn't perfect, I think it's very good given how young and fast-moving the language is. I usually have more trouble dealing with old C++ code on newer versions of G++/LLVM than Rust. Admittedly that C++ code often predates the very existence of Rust, but still, you'd expect a higher standard of backward-compatibility from such an old and widespread language.
If, on the other hand, the code was correct, then backwards compatibility is definitely expected. The same holds for the format of the cargo files, btw. changing something like this during one major (or minor, depending on the release cycle) release is bad form IMO.
The mentioned case of C compilers and their warnings is an interesting grey area. Presumably it would still compile just fine, but as a developer you want -Werror and -Wall. In practice this often makes certain libraries incompatible after a compiler upgrade.
This included:
- Soundness issues around Self: Sized bound requirements and traits. (Big breakage bout early one in rust lifetime.)
- Soundness issues around something related to match statements and guards I don't remember.
- Something related to accepting invalid inputs, I think this included Cargo accepting invalid Cargo.toml files.
There was also a very small bit of accidental breakage which counted as a bug but affected close to no one so no one ever invested time to fix. For example a bug which only affects people running multiple tightly linked rust crates on older editions and doing unusual stuff wrt. macro imports and re-exports which where only valid for I think 1-2 rust version.
Then there was a bit of accidental stabilization, where things which should not be doable on stable where doable. If not found very early this was normally kept stable if possible, but I think there was at least one time where this "stabilization bug" needed to be fixed.
Anyway to cut it short:
1) Compilers are not perfect, this is also true for C/C++.
2) At least serve bugs (e.g. soundness bugs) are not treated as "de-facto" standardized/stabilized but are fixed (something I prefer).
----
As a side note Firefox is a bad example as they used some tricks to use some definitely not stable options on stable rust which is basically guaranteed to cause breakage at some point and not covered by any stability guarantees.
As another side note `#[deny(warnings)]` is also not covered by stability guarantees, the compiler is free to add new warnings any time (but will do so with a lot of care, e.g. preferably only with new rust editions, which also may make some warnings errors).
---
Also:
-`rustup` supports setting overrides, no reason to fear updates, just pin a older rust version in your create using overrides (if you need to).
- Idk. about his problems but I have yet to see a case where any breakage wasn't reasonable straight forward to fix (weather unintentional, intentional to fix soundness or due to an edition change triggered by me).
Tons of regressions like this happen all the time in the GCC/LLVM world. Every time we do a bump of the default C compiler, you can bet it will take 4-8 weeks while regressions -- from "This random thing enabled -Werror, again" to "Someone used a new feature but scoped the #ifdef wrong" to "Actual segfaults in libstdc++" -- are sorted out.
Firefox is effectively targeting a nightly compiler, they just use this sneaky underhanded trick to access the features in a stable release.
Hmm. It could be nicer for stability reasons if Firefox pinned a specific nightly compiler rather than using a nominated stable release plus the RUSTC_BOOTSTAP trick. But I can understand why they would prefer the stable release.
Anyway: in short, the premise of this article is incorrect because Firefox cheated.
https://github.com/mozilla/gecko-dev/search?q=RUSTC_BOOTSTRA...
Specific example: https://github.com/mozilla/gecko-dev/blob/d36cf98aa85f24ceef...
In any case, sneakily using nightly features can make using a different Rust version problematic, since these features are free of any stability guarantees.
Arch Linux carries a rather large patch updating some vendored dependency in order to make Firefox 78 compile with a newer Rust. https://github.com/archlinux/svntogit-packages/tree/packages...
I would suspect the developers are maybe not entirely in tune with the Rust ecosystem, and that would make sense, they have more important stuff to do, like building Firefox, than playing with Rust. :)
Luckily as far as I know this is the only serious mis-use of the bootstrap flag. This would be a bigger problem if more people misused it. (And it has, in the past, when people used those packages. There was a relatively high-profile Debian situation about it a while back…)
The main users are encoding_rs which depends on packed_simd and qcms which needs NEON support.
So it's not like we're going around using all sorts of unstable features. It's very targeted and at least in qcms's case unlikely to change before stabilization.
Firefox no longer updates its MSRV in sync with Rust release trains. I’d be happy to remove this particular RUSTC_BOOTSTRAP if we decided to increase Firefox’s MSRV (currently stuck at 1.47.0).
The author seems to do this a lot.
From their perspective, who cares what the error is? They formed an intent, and then your stupid computer software instead of fulfilling their intent did something else. This is your fault.
For example maybe I wanted to delete the old data, and I typed "rm new-data". Don't tell me that deletes the new data, I can see that now, it's your fault that the new data is deleted, I wanted to delete the old data.
For a UX point of view, where possible try to offer "Undo". The user doesn't care that shooting themselves in the face was a bad idea, and they definitely don't care that you made them click "Yes, really, in my face, I understand this will hurt" three times first, now that they've done it, and it hurts, they want to undo it. Adding a forth confirmation step will not reduce the complaints.
Sometimes it will be hard to "undo". But you should periodically revisit decisions about that over the lifetime of a piece of software as resources increase. If you have 64KB bytes of RAM it's understandable if I can't undo "fill circle" in v1 of your Freeware art program. It's much less understandable in version 16 of the $100 program on a 16GB PC...
Would it make sense to be able to "undo" a Rustup even though that's difficult? Maybe. It's worth at least considering.
And yes, it is nice that Rust has a helpful community who might try to help you fix problems if you explain what the problem is, but this isn't really about Rust, it's about Users.
Therefore the onus is on Firefox to provide a good user experience.
Yes, but this would have been true even if there had been no rust errors or incompatibilities. You upgraded your rust compiler, so you're no longer comparing like with like.
I think that's pretty good, especially for such a young language with some ongoing significant changes. And I think that's stable enough for a production environment where there's an expectation of minimal ongoing maintenance. It means you rarely have to touch your code through updates, but occasionally will need to tweak something.
But it does add up, and something 5 years old is decently likely to not compile. I personally ran into something like that digging up an old project, and the problem is it's hard to incrementally fix it, since at that point all the dependencies are quite old, too. And there's a complex relationship between the dependencies snapshotted at that moment, and so probably you can just update them all to the very newest version and they'll be compatible, but somewhere in the middle is hard.
Which is fine for Firefox itself.
Just a problem for anyone forking of Firefox. (Or for other reasons trying to build it with a different rust version.)
There are some project more focused on that but even them eventually give up on supporting all versions of their deps. Likewise for compilers, pretty much regardless of the language. I mean the only way to stay perfectly compatible with random codebase is to keep the compiler even bug compatible with older ones, which is not particularly interesting...
gcc seems to not always be backward compatible in practice. clang too. msvc too.
Sometimes I feel that C is the only language that can benefit from compound gains over time, their spec is tight and sees sparse nuanced improvements in wide intervals of time.
- firefox "secretly" relying on unstable things while using stable rust (using some magical undocumented rust flags meant to be only used for bootstrapping rust).
- the older firefox release relying/touching on some "bug" in rust which was fixed (e.g. accidentally accepting malformed config files, or soundness issues).
- a bug in the compiler no one put resources to fix in yet (as e.g. close to no one is affected by it and there are more relevant bugs to work on).
This can happen to you in C or C++, too.
For any larger and more complicated projects some bugs "popping out" when using newer compilers due to either compiler bugs or code bugs "somehow passing by" in earlier compiler versions is totally a thing.
Through due to how long the C standard stood still it's not that often happening in C, I guess? Hm, I wonder if Linux kernel devs might disagree??
Back in the GCC 4 timeframe (~2005), the compiler started taking a more strict view of the C standard in a way that produced additional warnings/errors relative to the GCC 3.x series. Compiler upgrades do regularly break some subset of packages in distro repositories; breaking C++ happens a lot more than C, but it happens.
Except many people doing exactly that and it working fine.
> backwards compatibility problems
Which aren't really a problem in practice. Definitely not worse then in any other language, yes there was some intentional breakage (soundness bug fixes) and unintentional breakage (compiler bugs) but most of this was in the very early stable rust versions since then accidental breakage which isn't caught and fixed before stable has become quite rare. As far as I know rust does more to find accidental breakage then many other languages.
> They always use $latest and what is in $latest changes so fast even if your rustc is 4 months old it already can't compile code written in $latest.
What is the problem with requiring a up-to date build chain? Slow long release cycles with a lot of patch back-porting is a software development model which for many (most?) projects has been deemed to not work well and has been replaced with something more in the direction of CI. Let's be honest even if we assume slow long releases are better for compilers, they still put quite an additional burden one the compiler development process which a is feasible if you have a lot of payed devs from large companies, but not really if only your core devs are payed (by the foundation and/or companies) but a lot of relevant contributions come from people not directly payed to work on rust.
> But right now it's painful and full of pitfalls.
I don't think so, but I guess we both can agree that thinks are improving, for me to make it even better and for you to maybe make it usable for the things you currently treat it as not suited for. ;-)
Well, Debian Bullseye, a distro that hasn't even been released yet, already has a rustc that's so out of date (ie, ~5 months, 1.48) that it can't compile code written today using today's features. I guess if you come from the web or some "fast" internet company where you rebuild your entire world every day then 5-6 months is an eternity. But with, say, c++, I can expect to be able to compile C++ programs written by devs for a handful of years before C++?? issues start. And despite Bash 5 getting new features regularly I can expect a bash script written today to work on my 15 year old desktop running linux 2.6. It's not because these languages don't add new backwards incompatible features. It's because the devs don't immediately use them and say, "What is the problem with requiring a up-to date build chain?"
If you only talk to other bleeding edge devs in the rust community I can see how this ethos propagates. But not everyone is a bleeding edge rust dev. Some of us run our distros for many years and just want to compile programs.
Requiring the use of rust-up instead of using system repos is the often suggested way around this. But I, and many others, absolutely hate using third party compiler software from out of repos. It's a bad practice and leads to it's own trouble as shown the very topic this discussion thread is about.
It's not a problem of running rust code, you always can run any "latest" compiled rust code with Debian Bullseye. Only for building the binary do you need a up to date version.
But this isn't uncommon for a lot of projects which might require up to date build tools for them to be build.
I my opinion, and I know many do disagree with me, the problem is the idea of pinning dev-tools when releasing a distribution and expecting this to work well. In my experience it doesn't, and it's not limited to rust or web-related things where it doesn't work well.
(Like e.g. I don't know how often I had to install newer versions of clang, gcc, C++-related build tools, python related build tools, system dependencies, java etc.)
This sounds more like an argument against Debian’s packaging practices than Rust. Debian is full of out-of-date tools, and that and CentOS doing the same are the reason non-system package managers are so popular.
- Node.js
- Python
- Go
- Rust
- C++
- .NET Core
- Java
None had an up-to-date version in the distribution repositories. It’s a problem - and for those who defend it as otherwise while lamenting the rise of Docker, perhaps it’s worth some careful introspection about what a majority of people actually want from an operating system distribution, and whether the concept is in fact a relic of the days when software was distributed on CDs via the mail.
> None had an up-to-date version in the distribution repositories.
Of course not. A Debian release is meant to not change – except for security fixes – for the duration of its 2 year cycle. That's a feature, not a bug.
Operating systems are servant of their applications, not vice versa.
Sadly, Debian does this a lot - Bullseye is going to release with NodeJS v12 which is already in Maintenance LTS (dying) and not the Active LTS (v14) and has 4 more newer Current releases (15->18). I am a Debian supporter but this is a distro problem of locking in on already out of date dev tools over and over again, Bullseye is already far out of date before it's even launched.
https://packages.debian.org/search?keywords=nodejs&searchon=...
> This is what Debian's Stable name means: that, once released, the operating system remains relatively unchanging over time. [1]
If you just want to compile programs, perhaps a distro + release channel geared towards production server deployments isn't the best choice. They even call out that fact in the "Why Debian" page:
> You can fine-tune the degree of risk you want to take, since Debian has three separate releases: Stable, Testing, and Unstable. On some of our machines (the server, the kiosk machines) we run 'stable'. Some of the other systems (individual work-stations) run various combinations of testing/unstable. (Note that there are no security updates for testing). I run some stuff from experimental on my own work-station. What's great is the ability to make finely graded decisions for different machines serving different functions. But even the more adventurous choices are solid enough that they virtually never break. And `stable' just never breaks ;-). [2]
[1] https://wiki.debian.org/DebianStability
[2] https://wiki.debian.org/WhyDebian#Why_Linux.3F_Why_Debian.3F
My Rust 1.0 code on the other hand has compiled just fine since the dawn of stable rust.
The only really stable languages might be C89, C99, some fortran, and Cobol.
I think the Rust team does a fantastic job of supporting old code bases with new compilers. Crater is a testament to how much they care in fact.
As for another ancient Rust project which was also on 1.0 when I was trying the language out, the borrow checker on 1.52.1 screeched at every line of my code.
While C++ is just as bad, the fact that Firefox must always use the latest Rust compiler gives you a hint that it will not work on lower versions; hence the need of a MSRV. (Minimum Supported Rust Version)
Of course, without more info nobody can tell which of these or even whether it is one of these two or something else.
The only real pain we felt was around iOS bitcode and that was mainly Apple's fault because they make the whole process so Byzantine if you aren't using clang.
The question then is how does someone define backward compatibility?
The answer is a formal definition of what the expected behaviors and expectations of the programming language in question. This usually means a language specification or extensive unit tests that effectively define what the programming language should or should not do for a given set of circumstances.
As someone who's invested a lot of time in PLT and even at one point had their name on a draft of the Haskell standard, the unfortunate reality is that language specifications are very often not useful to the wide majority of people, while being quite laborious to produce and maintain and work with over time. The culture of computer programming, at large, is part of this, it's not some mathematical fact, but a social one. At the end of the day, what constitutes "Rust the language" in any formal capacity doesn't matter; "Rust the language" is a social construct in that sense, a guarantee Rust will still be Rust even if some minor bugs are fixed that they let slip by. This is how most people think of it, when you get down to the nitty gritty.
I'll also say that tons of regressions happen in mature languages with backwards compatibility. Ask any Linux distro maintainer! I don't even keep track of all the weird reasons regressions happen anymore; it's like counting sand on a beach. It's not a happy fact of life, but it is what it is, and regressions do happen, no matter if your language is stable for 5 years or 50.
What you are describing is the less fun and sometimes tedious nature of software maintenance.
It can be thankless work, but it is very important work to ensure that a programming language is reliable and to minimize the regressions that sometimes occur.
It is very much a social contract, but essential so that expectations are met, particularly for systems programming that Rust targets.
I wonder if anyone has ever tried to create a literate programming-style specification for programming languages--something that combines the high-level descriptions of programming language semantics with executable unit tests. Such a system would provide detailed semantics and give programming language creators and maintainers a good way to prevent regressions and create RFC branches to try out and test new ideas. It would help to bring the social constructs of the programming language closer to the technical implementation semantics.
The Ada programming language has a specification: http://www.ada-auth.org/standards/rm12_w_tc1/html/RM-TTL.htm...
and a conformity test suite: http://www.ada-auth.org/acats.html
which ensure that any Ada implementation conforms to what the specification requires.
Does anything similar exist for other programming languages? If so which languages have conformity tests or similar?
I use Ada professionally and Rust as a hobbyist. Rust is moving very fast and I would like to see it mature for use in safety critical domains.
Also, thank you for your work on Haskell.
Some quotes from the linked page:
> Ferrocene is a sustainable effort led by Critical Section GmbH, a newly formed Ferrous Systems subsidiary, tasked with making Rust a first-class language for mission and safety-critical systems. Ferrocene produces a qualified Rust compiler toolchain. General availability is expected by the end of 2022.
And also:
> Ferrocene is a vehicle for a versioned Rust and MIR specification, paired with automated verification of Rust semantics. These "runnable specs" give developers in mission-critical and high-security environments a sound, proven, and addressable foundation for building critical libraries, analysis tools, and further system assurances.
And also:
> Critical Section will maintain designated legacy versions of the Rust toolchain and supporting utilities. This support includes backporting fixes of critical language and standard library issues (performance bugs, unsoundness, security) and maintaining key rustc targets, including test cases and platform-specific testing.
And finally:
> Ferrocene is a principled project with much work ahead, requiring cross-industry collaboration and continuous feedback. It has support from crucial industry partners and subject experts, and we're building a thoughtful community.
> Right now, Ferrous Systems and Critical Section are calling for additional partners to join this effort. We're currently looking for partners from diverse industries
It is informative for example to review talks Bjarne gave about C++ Concepts in 2015 compared to the C++ 20 feature Concepts. That's five years to get something similar in scope into the actual language standard, and of course since this isn't exactly what Bjarne designed, you then to have transition compilers and codebases to the actual standard feature...
I'm dubious about the value of conformance testing as a deliberate exercise. In practice real world implementations will definitely be defective and it's important that defects are cured when identified, mostly by changing implementations but sometimes in reality by changing the specification to reflect what is implemented. But I don't know how much value I believe there is in somebody sitting down to try to construct a test suite starting from the standards documents, rather than gathering minimal samples of real world code that did not do what the standard says it should do so that implementations can check they get those right (and where appropriate the standards body can reconsider whether the standard itself is wrong here). It appears to me that ACATS tries to do the former, right?
This is in contrast to C and C++ compilers like GCC and Clang, where the default "std" setting is often updated with new releases.
With the slight caveat that this is the "default if not specified", but the default when creating a new project with "cargo new" is to specify the currently latest version on the generated Cargo.toml file.
That is, there are two defaults: the default for pre-edition projects (forever 2015), and the default for creating a new project (sets the project to what was the latest edition when the "cargo new" was run).
I mean you can mix and match dependencies of different editions, and there are (IMHO) very few reasons to start a new project with an old editions. For that (IMHO) very few reasons you tend to be aware of you intentionally wanting to use a old version and as such it's not a problem.
Through I would say there is no real default rust editions, it's more appropriate to say crates/libraries "keep" the rust edition they where created with until explicitly changed.
Version: 1.0.0,.., 1.50.0, 1.51.0, ... Edition: 2015, 2018, ..
Versions are the compiler version you use.
Editions are more like a language dialect, and all newer compilers support all "older" editions.
What versions change tend to be:
- config defaults, e.g. make some warnings errors, change the default dep. resolver, etc.
- syntax (in minor ways). E.g. add a new keyword for a new feature/syntax or remove a reversed but unused keyword where it's no clear that it's not needed.
- improvements the the borrow checker or import system which in some edge cases could break existing code.
Anyway any rust version supports all editions released up to the point in time and you can freely mix and match them between dependencies.
Mainly to fix soundness holes in the language. Like there was some problem with match statements, guards, moving values and potential (I think?) user after free or so. Which required match guards to be slightly constrained in some edge cases.
This caused cargo to install different minor versions than it would have a year ago despite a lock file being present.
These minor version updates were relying on newer rust version features and then failed to build on the old version of the rust compiler I was using.
Updating rust made the build work again, but now with additional warnings (where the code I was compiling did shady things which old rust wouldn’t detect but new one did), but it did finally produce a usable binary.
Still, I was a bit miffed by cargos behavior of ignoring minor versions locks in the lock file and installing later versions than what was locked.
What good is a package lock file if it isn’t respected?
This particular issue is in regard to `cargo install`: https://github.com/rust-lang/cargo/issues/7169
Oh, or, I guess, it might be how the version is specified--guess we need some more details on what "compile something after it wasn’t touched for a year" entailed.
Both behaviors can be surprising. We picked one. It happens. I am not sure myself if it was the right choice or not.
I had the same experience trying to use older versions of Rust or trying to use newer versions of rustc on older crates. It doesn’t “just” work. For many of us this coming from Go, this seems like a step backwards.
I’m thankful OP had the courage to post this note about his experience. I’m also disappointed to see members of the Rust community to suggest its a failing on OP’s part that they were not able to get Firefox to build and that bringing up this difficulty is something OP was wrong to do.
But instead of taking a moment to read and understand the error messages, or ask the community for help with understanding them, OP chose to publish and popularise a blog post without including the error messages (or any context that would help anyone to make up their own mind based on actual facts).
Courage is not the word I would use.
I don't think its apt to call the people critiquing the critique "apologists" when the entire original blog post seems to be misunderstanding on the part of the blogger. Posts that critique things the poster do not understand tend to themselves generate critical feedback which seems fairly measured to me.
I have read the blogpost... and this isn't really the substance of the complaint? It is Mozilla's fault really to be honest, they should be sticking to a nightly if they want to use weird and non-standard features.
I personally think the only similarly between yours and the authors' is the (supposed) unmet promises of backwards compatibility - guess what, I code in different languages (including Go) and I can see breakages regularly, mostly not due to breaking a "backwards compatible" promise but because the broken code has been always there, just not being enforced by the compiler. Specifically in Rust, the backwards compatibility promise wasn't Microsoft's "everything including undocumented features" backwards compatibility: it was only limited to documented features used properly. This can surprise people who relies on the Microsoft definition of backwards compatibility.