Disclaimer: I'm not a Rust author, but I actively follow the development process.
Rust does use semver. But as the concept of "breaking change" is ill-defined – as I've understood – in the definition, the Rust project is using the following heuristic:
* If it's a safety/soundness-releated bugfix, it's considered a minor (1.x.0-changing) change even if it makes some existing crates stop compiling. Because the crate was relying on broken/unsound functionality, that's acceptable.
* Impact of all such breaking changes are measured and assessed against the ecosystem using the Crater tool.
* Even if crates stop building, the changes should be such that it's trivial to fix them. (In the best case, one-liner type annotations etc.)
Btw. Rust sometimes deprecates APIs that have been supplanted by better alternatives. Deprecations are warnings, and they are allowed to go away only on a x.0.0 version change. Additionally, careful consideration for stability and breaking changes is done when designing new language features.
Rust does use semver and takes it very seriously. RFC 1105 for example documents exactly what is considered a breaking change to stabilized library APIs. The Rust team has an explicit allowance for breaking changes related to type soundness or memory changes without performing a semver bump. This is the first such change, there is one more coming in the future.
Both changes are very small; they do not impact most users and the users who are impacted can easily fix them unless they are exploiting the existing "bug" to do something unsound.
So, this is kind of a complex topic. The sort of TL;DR is, Semver itself does not actually specify things, it just says "compatibility". Specifically,
> MAJOR version when you make incompatible API changes,
But does not define what "compatible" means.
In a statically typed language, virtually any change, including an addition, can be construed as a "breaking change". For any proposed change to Rust, I could write code that that change would break. Adding a new method is a breaking change, for example, because I could have written a method with that name previously.
Note that this is also consistent with other languages: Java, for example, regularly makes very small breaking changes, even though it's widely regarded as a language which takes compatibility extremely seriously.
> This RFC proposes that minor releases may only contain breaking changes
> that fix compiler bugs or other type-system issues.
Without a formal language spec, and with certain bits of the language being generally under-specified, you can language lawyer forever about a term like "breaking". That's why we went through all of the possible ways that we could change the language, and spelled out what kinds of versions we're allowed to make those changes in. I would argue that Rust is far more clear about its version guarantees than many, many other languages.
It's important to remember that semver is a _social_ tool to communicate roughly what has changed. If this blog post were about a Rust 2.0, you would rightfully assume that it's going to be tough to upgrade to. That's not the case here. The actual impact of this particular change to the ecosystem is virtually nothing.
All methods are namespaced but there is sugar which can cause a conflict because it searches the namespaces in scope for methods.
Imagine the trait (namespace) `Baz` defines a method `bar()`, and `Foo` is a type which implements `Baz`. `foo.bar()` is sugar for `<Foo as Baz>::bar(foo)`. Imagine `Foo` also implements `Quux`, another trait in scope, and `Quux` is extended to also define `bar()`. Now `foo.bar()` is ambiguous, because it could also mean `<Foo as Quux>::bar(foo)`. As you can imagine, though, this happens very rarely.
You can't avoid a conflict unless you require specifying the namespace at every call-site, which is in my estimation a much greater burden than editing your code to use the explicit namespace form if a conflict arises because of a new method in a dependency.
We have a module system, but that doesn't help. You can still define methods on arbitrary types, subject to the coherence rules.
You can still write out a disambiguated form to distinguish the two, but it's not the default way. That's one of the reasons why adding a defaulted method to a trait is a minor change. See https://github.com/rust-lang/rfcs/blob/master/text/1105-api-... for more.