What is backwards compatibility?


#1

In some cases, it is easy to see that a change is backwards compatible. However, many changes that seem backwards compatible are not.

On the other hand, some kinds of changes rarely break code. We have to decide whether these are considered to be “practically backwards-compatible”.

Here are some edge cases:

  • Adding pub items to a pub mod

  • Causes name clashes due to glob imports

  • Adding pub fn inherent methods to a pub type

  • Causes name clashes with trait methods.

  • Adding object-safe provided methods to pub traits

  • Causes name clashes with inherent methods due to autoderef / autoref

  • Adding #[must_use] to a type or a fn

  • Breaks code with #![deny(unused_must_use)]

  • Implementing Drop for a type

  • Breaks unsafe code that assumes no Drop impl’s

  • Adding private fields

  • Breaks some unsafe code, e.g. transmute() and Vec::map_in_place().

  • Changes the result of size_of().


#2

In general adding any public anything is a breaking change when you factor in glob imports. e.g. adding a new module can cause a naming conflict. Adding a trait can add new methods creating ambiguity or just create a conflict with a different type in scope. Making functionality more generic can break inference.

If we add negative bounds then implementing a trait can unimplement another. Rust’s design is basically a back-compat nightmare if you’re strict about the definition of back-compat. The core team has been working on a definition that includes “acceptable breakage” like having to use UFCS to disambiguate. Basically I think the spirit is that a change isn’t really breaking if it doesn’t require you to change the spirit or design of your code. (so removing a function or making a function no longer take a concrete type is breaking, but needing to fiddle with imports/UFCS isn’t).

@aturon would know better than me, though.


#3

@Gankro Note that even adding a public method to an existing type can silently break code, even without glob imports.


#4

@aturon gave me this link in IRC: https://github.com/rust-lang/meeting-minutes/blob/master/workweek-2014-08-18/api-evolution.md


#5

If you are adding #[must_use], you are probably fixing a bug and the user of your type/function was using a bug. There should be no backwards-compatibility guarantee for bugs in my opinion.


#6

Isn’t there? “Bugward-compatibility” is very real, in some systems. It’s fine to decide not to do it for the Rust language and standard library, but it should be an explicit, documented decision.


#7

yea, sorry. I’ve been expressing an opinion as a fact again :confused: edited the post.