I'm sorry to say but you must be delusional. Can you list the these `aggressive` breaking changes? There were only 7 releases since go1 and I fail to find any breaking change in the language specification. On the other side each Rust release has a fat list with "BREAKING CHANGES". Many Rust packages only work with specific Rust versions(i.e. nightly). The rust ecosystem(std lib, tools etc) is also way behind Go in terms of stability. Why do you need rustup if Rust is so backward compatible? I'm not saying that Go is a better language than Rust but the its ecosystem and dev experience is definitely superior.
> Can you list the these `aggressive` breaking changes?
Changing the size of int. Changing methods to introspect the type of their arguments and do things differently. And so on.
> There were only 7 releases since go1 and I fail to find any breaking change in the language specification. On the other side each Rust release has a fat list with "BREAKING CHANGES".
Because we have a very specific definition of "breaking change" that is primarily concerned with the practical effect of changes we make. Go does not consider these changes "breaking". Using Go's definition (changes to the language definition), we have no "breaking changes".
If we were to change the size of int (something we will not do, by the way, due to the practical effects of making such a change), then we would list it under "breaking changes", even if we were technically allowed to do it. That's because we care about the practical effects of our changes, not just the letter of the language definition.
> Many Rust packages only work with specific Rust versions(i.e. nightly).
Because they are explicitly opting into unstable features that are carefully marked as such. We can't stop packages from doing that. Nor can any other language.
> The rust ecosystem(std lib, tools etc) is also way behind Go in terms of stability.
The parts of the Rust standard library that are marked stable have remained completely backwards compatible, in both interface and implementation.
> Why do you need rustup if Rust is so backward compatible?
Because it's nice to keep your compiler up to date and to target different platforms?
You are right about the compiler changes but even so you can't compare 1-2 compiler BK with Rust which has language changes as well. Compiler changes are the norm in Rust.
>> The parts of the Rust standard library that are marked stable have remained completely backwards compatible, in both interface and implementation.
Rust doesn't have backwards incompatible language changes.
> This looks like a breaking change on a stable API.
No. It is a method addition. It is breaking only in the sense that code that didn't explicitly invoke the previous "as_ref" method might call this new method instead. It's the moral equivalent of:
type A struct { ... }
type B struct {
A
}
func (a ﹡A) Foo() {}
and then a later version of Go adds a method:
func (b ﹡B) Foo() {}
Such that code that called Foo() on an instance of ﹡B might call the new method instead. Go can make those changes.
Compiler changes are the norm everywhere? I'm not sure what you're trying to say here.
> This looks like a breaking change on a stable API. Am I wrong?
Yes and no.
Rust's policy on breaking changes is that changes that can be fixed by properly qualifying an implicit path are not breaking. Otherwise, adding any method to anything would be a breaking change. In this case, you can use the UFCS syntax to disambiguate.
So it's an "allowed" kind of breakage because not allowing this means freezing the stdlib.
(Go doesn't have this issue due to lack of generics, overloading, and interface-based overloading. Edit: actually, go does too, due to inheritance, but that is easier to avoid and isolate. In rust you can always write client code that breaks if the stdlib adds a method, anywhere. This is true for most typed languages).
Anything that has the chance of practically breaking things is still run through crater (which tests impact on the ecosystem) and as you can see that PR had minimal impact.
Steve has pointed out that go did have this issue as it has a limited level of auto-deref, which it has changed in the past: implementations performed two levels of auto-deref when executing methods when the spec only requires one, the implementations were changed to only allow a single auto-deref: https://golang.org/doc/go1.4#methodonpointertopointer
Right. However, as I mentioned in the edit, you can still be careful about avoiding breaking changes through inheritance and autoderef in the evolution of Go's stdlib. It forbids very specific types of methods from being added, and if you avoid that, you can continue to add methods as if nothing is wrong.
Rust (and C++, because SFINAE, and many other languages), on the other hand, technically has a breaking change each time any method is added to any public type in the stdlib. It's always possible that the client lib was using a trait method of the same name, and now has to fully qualify the method call.
https://doc.rust-lang.org/reference.html , which is accurate, but not always 100% up to date with the latest RFCs. There's also work on a formal, proven specification of the memory model, but that's not done. It'll be a while.
That's understandable. I've just tried to prove the point that Rust is still a language in flux compared with Go, hopefully making the Rust team aware why some developers hesitate to use Rust on new projects.
> I've just tried to prove the point that Rust is still a language in flux compared with Go, hopefully making the Rust team aware why some developers hesitate to use Rust on new projects.
That's not what I've seen from your comments. Instead I've seen some confused arguments about what "prose only" means (anyone in the PL field would consider both Rust and Go's documentation "prose"), combined with incorrect statements about both Rust and Go and a completely baseless assertion that Rust is "a language in flux".
Rust uses a much stricter definition of breaking change than does Go. As discussed elsewhere in this thread, Go changed the size of integers. While this is technically allowed by the language (it wasn't previously specified), and it shouldn't break conforming code, it can break code that depended on the previous size.
The Rust maintainers would have considered this a breaking change. The Go maintainers did not. This isn't to say either side is right or wrong, just that they are measuring different things.
Additionally, the Rust maintainers have been exceedingly cautious whenever making these types of changes. They literally download, compile, and test all published crates to look for indications that such a change might actually break existing code. In the very few cases it has, they've worked with crate authors to incorporate fixes.
The very low bar Rust sets for determining what is a breaking change directly reflects the extreme regard they have for this issue.
> Why do you need rustup if Rust is so backward compatible
To cross compile. To have quick toolchain updates. To get bleeding edge compiler improvements (e.g. speed) quickly. To test out new features. To help find bugs in the compiler.
One very common use case of rustup is to use clippy. Clippy is a developer tool which hooks directly into the compiler and uses all sorts of private APIs, an inherently unstable thing. It only works on nightly. Lots of people write their code to work on stable, but want to use this tool so they use rustup. Note that no language has a stable way of hooking into the compiler.
Very few rust packages only work with nightly. Care to provide some examples?
> Why do you need rustup if Rust is so backward compatible?
For one, testing on various versions of Rust. For example, I have a kernel project that's pinned to a particular nightly version, while the rest of my projects build on stable. Rustup makes this Just Work.
Well, that's my point! You shouldn't need to test various versions of Rust if you there is a strong backward compatibility policy. I might be mistaken but my feeling is that most of the rust devs are using the nightly version thus the reason of a tool to debug/test different versions.
He didn't say he was "testing" with nightly: some experimental features only work on nightly (which, being experimental, the features may change in breaking ways, but that's why people have to opt-in to using a nightly) and so if one of your project needs one of these features, you can use rustup to get nightly for just that project and the stable releases for the rest of your work.
A staged release cadence with different levels of surety gives people the ability to play with features as they're developed to make sure those features solve the problems they're trying to solve (in the best way) by giving time for real-world experimentation and feedback. A feature can graduate from nightly-only to stable, and it then has a strong backwards compatibility requirement. The nightly experimentation period is valuable to get those features perfected before people can start relying on them more broadly.
What if I would like to guarantee this property for my own code? As well as testing each nightly as they come out, in case something accidentally breaks, so it can be fixed before a release? This tooling assists greatly with that.
(And dbaupp is correct that it's not always about testing; not all of the OS dev features are in stable yet, so nightly is the only option for that kind of project.)
Right, but humans are fallible. Bugs happen. It's a good idea to test early and often, just to make sure: Travis runs are extremely cheap. Better to catch accidents before they make it into an actual release. More testing doesn't hurt anyone.
I would like the code developed now to work with all subsequent rust releases until 2.0 so that I can take advantage of improvements to the compiler and std libraries without any additional effort.
A small effort may effort may be required if there were security/critical bugs.
> I would like the code developed now to work with all subsequent rust releases until 2.0 so that I can take advantage of improvements to the compiler and std libraries without any additional effort.