🧊

Rustのcoherence/orphan ruleについて

biome_formatterbiome_js_formatterのコードを調べてて知った。

どういうものか

  • crateAで定義されてるtraitを
  • crateBで定義されてるstructに実装したい

という場合にこれができるのは、createAかcrateBだけ。

crateCのような別の場所から、外部のものを勝手に実装することはできない、とのこと。

// error[E0117]:
//   only traits defined in the current crate can be implemented for types defined outside of the crate
impl std::fmt::Display for Vec<u32> {
  // ...
}

というように、勝手に外部のものに実装することはできない。

これが許されてしまうと、あっちでもこっちでも同様の実装が行われたとき、どっちが正しいのかわからんでしょって書いてあった。

https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type

coherenceと呼ばれる特性の一種であり、orphan ruleと呼ばれるらしい。

どうする

愚直な解法としては、

  • crateAで、crateBのstructに対して、自身のtraitを実装する
  • crateBで、自身のstructに対して、crateAのtraitを実装する

とのこと。

まあそりゃそうかって感じだが、そういうことがしたいのではない。crateCで実装したいのである。

というわけで紹介されてたのが、このNewtypeパターンと呼ばれるもの。Haskell由来らしい。

https://doc.rust-lang.org/book/ch19-03-advanced-traits.html?highlight=orphan#using-the-newtype-pattern-to-implement-external-traits-on-external-types

つまりはcrateBのstructをラップした”新しいstruct”をcrateCで用意して、そこでcrateAのtraitを実装する。

struct Wrapper(Vec<String>);

impl std::fmt::Display for Wrapper {
  // ...
}

これならWrappaerはローカルなstructなので、ルール違反にはならない。

ただし、ラップしてるがゆえに、

  • 自身にはself.0でアクセスしないといけない
  • 外部からも本来のメソッドは見えてない

というところで、必要ならDerefでなんとかしろとのこと。

https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence

感想

Rustむずかしいね!

Ixrec/rust-orphan-rules: An unofficial, experimental place for documenting and gathering feedback on the design problems around Rust’s orphan rules https://github.com/Ixrec/rust-orphan-rules

こういうのもあった。