What exactly is considered a breaking change to a library crate? Ask Question

What exactly is considered a breaking change to a library crate? Ask Question

Rust crates use Semantic Versioning. As a consequence, each release with a breaking change should result in a major version bump. A breaking change is commonly considered something that may break downstream crates (code the depends on the library in question).

However, in Rust a whole lot has the potential of breaking downstream crates. For example, changing (including merely adding to) the set of public symbols is possibly a breaking change, because downstream crates can use glob-imports (use foo::*;) to pull symbols of our library into their namespace. Thus, adding symbols can break dependent crates as well; see this example.

Similarly, changing (adding or changing the version) the set of our dependencies can break downstream builds. You can also imagine that the downstream crate relies on a specific size of one of our public types. This is rarely, if at all, useful; I just want to show: everything could be a breaking change, if only the downstream crate tries hard enough.

Is there any guideline about this? What exactly is considered a breaking change and what not (because it's considered "the user's fault")?

ベストアンサー1

There is a Rust RFC on this subject: RFC 1105: API Evolution. It's applicable to any Rust library project, and it covers all kinds of changes (not just breaking changes) and how they impact semantic versioning. I'll try to summarize the key points from the RFC in order to not make this answer a link-only answer. :)

The RFC acknowledges that pretty much any change to a library can cause a client to suddenly stop compiling. As such, it defines a set of major changes, which require a bump of the major version number, and a set of minor changes, which require a bump of the minor version number; not all breaking changes are major changes.

The key attribute of a minor change is that there must be a way that clients can avoid the breakage in advance by altering slightly their source code (e.g. change a glob import to a non-glob import, disambiguate an ambiguous call with UFCS, etc.) in such a way that the code is compatible with the version prior to the change and with the version that includes the change (assuming that it's a minor release). A minor change must also not force downstream crates to make major breaking changes in order to resolve the breakage.


The major changes defined in the RFC (as of commit 721f2d74) are:

  • Switching your project from being compatible with the stable compiler to only being compatible with the nightly compiler.
  • Renaming, moving or removing any public item in a module.
  • Adding a private field to a struct when all current fields are public.
  • Adding a public field to a struct that has no private fields.
  • Adding new variants to an enum.
  • Adding new fields to an enum variant.
  • Adding a non-defaulted item to a trait.
  • Any non-trivial change to the signature of a trait item.
  • Implementing a fundamental trait on an existing type.
  • 既存の型パラメータの境界を厳しくします。
  • 関数への引数の追加または削除。
  • RFC にマイナー変更として記載されていないその他の重大な変更。

RFCで定義されているマイナーチェンジ(専念721f2d74(指定がない限りは改行)は次のとおりです。

  • クレート上の貨物機能の使用方法を変更します。
  • モジュールに新しいパブリック アイテムを追加します。
  • 構造体に少なくとも 1 つのプライベート フィールドが既に存在する場合 (変更前と変更後)、そのプライベート フィールドを追加または削除する[壊れない]
  • すべてのプライベート フィールド (少なくとも 1 つのフィールドを含む) を持つタプル構造体を通常の構造体に変換するか、その逆を行います。
  • デフォルトのアイテムを特性に追加します。
  • デフォルトの型パラメータを特性に追加する[壊れない]
  • 既存の型に非基本的特性を実装する。
  • 固有の に任意のアイテムを追加しますimpl
  • 関数の文書化されていない動作を変更する。
  • 既存の型パラメータの境界を緩める[壊れない]
  • 型または特性にデフォルトの型パラメータを追加する[壊れない]
  • 既存の構造体または列挙型フィールドの型を、デフォルトで以前の型になる新しい型パラメータに置き換えることで一般化する[中断まで問題 27336固定されています]
  • 既存の関数に新しい型パラメータを導入します。
  • 既存の関数のパラメータまたは戻り値の型を、以前の型にインスタンス化できる新しい型パラメータに置き換えることによって一般化します。
  • 新しい lint 警告/エラーを導入します。

見るRFC説明と例については。

おすすめ記事