web-dev-qa-db-ja.com

newtypesにDerefを実装することは悪い習慣と考えられていますか?

私はしばしばnewtypeパターンを使用しますが、my_type.0.call_to_whatever(...)を書くのにうんざりしています。 Derefトレイトを実装するように誘惑されます。これは、いくつかの状況で基礎タイプであるかのように新しいタイプを使用できるため、より単純なコードを記述できるためですe.g。

use std::ops::Deref;

type Underlying = [i32; 256];
struct MyArray(Underlying);

impl Deref for MyArray {
    type Target = Underlying;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

fn main() {
    let my_array = MyArray([0; 256]);

    println!("{}", my_array[0]); // I can use my_array just like a regular array
}

これは良い習慣ですか、悪い習慣ですか?どうして?欠点は何ですか?

25
Boiethios

悪い習慣だと思います。

私のnewtypeは、状況によってはそれが基になる型であるかのように使用できるので

それが問題です—暗黙的に基になる型として使用できますいつでも参照です。 DerefMutを実装すると、変更可能な参照が必要な場合にも適用されます。

基礎となる型から何が利用可能で何が利用できないかを制御することはできません。すべてです。あなたの例では、人々が _as_ptr_ を呼び出すことを許可しますか? sort はどうですか?彼らができるので、私はあなたがそうすることを望みます!

できることは、メソッドを上書きすることですが、それらはまだ存在している必要があります。

_impl MyArray {
    fn as_ptr(&self) -> *const i32 {
        panic!("No, you don't!")
    }
}
_

それでも、明示的に呼び出すことができます(<[i32]>::as_ptr(&*my_array);)。

コードの再利用に継承を使用することは悪い習慣であると私が信じるのと同じ理由で、私はそれを悪い習慣と考えています。あなたの例では、あなたは本質的に配列から継承しています。私は次のRubyのようなものを書くことはありません:

_class MyArray < Array
  # ...
end
_

これは、オブジェクト指向モデリングのis-aおよびhas-aの概念に戻ります。 MyArrayは配列ですか?使用できるようにする必要があります配列が可能な場所?消費者が壊れてはならないということをオブジェクトが支持するという前提条件はありますか?

しかし、私はmy_type.0.call_to_whatever(...)を書くことにうんざりしています

他の言語と同様に、正しい解決策は継承よりも構成することだと思います。通話を転送する必要がある場合は、newtypeにメソッドを作成します。

_impl MyArray {
    fn call_to_whatever(&self) { self.0.call_to_whatever() } 
}
_

Rustでこれを苦痛にさせる主なものは委任の欠如です。A架空の委任構文は次のようになります

_impl MyArray {
    delegate call_to_whatever -> self.0; 
}
_

したがって、shouldを使用する場合、Deref/DerefMutスマートポインタを実装するときだけが理にかなっていると私は主張します。


実際に言えば、私はdo公開されていないnotnewtypesに対してDeref/DerefMutを使用します私が単独または過半数の貢献者であるプロジェクトで公的に。私は自分を信頼し、私が何を意味するのかをよく知っているからです。委任構文があったとしても、私にはありませんでした。

21
Shepmaster