biome_formatter
とbiome_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由来らしい。
つまりは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
こういうのもあった。