🖐️
Rust でインターフェイスを切るベストプラクティス(1)
インターフェースは Unsurprising であるべきである
以下 Rust for Rustaceans 3章を要約
ユーザがインターフェースに暗に期待していること
-
iter
は&self
を引数にとる。 -
into_inner
はself
を引数にとる。 -
SomethingError
はstd::error::Error
を実装する。 - Trait は基本的に
Debug
を実装する。- ユーザはオブジェクトを
println!
できることを期待するため。
- ユーザはオブジェクトを
- Trait は基本的に
Send
とSync
を実装する。- ユーザはオブジェクトを
Mutex
で囲むことができることを期待するため。 - できない/しなくて良いならその理由をドキュメントに明記する。
- ユーザはオブジェクトを
- Trait は基本的に
Clone
とDefault
を実装する。- できない/しなくて良いならその理由をドキュメントに明記する。
- オブジェクトに対して
assert_eq!
を呼ぶこと期待される場合はPartialEq
を実装する。- その他
PartialOrd
,Hash
,Eq
,Ord
についても適宜実装する。
- その他
- オブジェクトが
std::collection
のキーとして用いられる場合はPartialOrd
とHash
を実装する。 - オブジェクトの Serialization/Deserialization が行われることが予想される場合は、 Serde
Serialize
,Deserialize
を実装する。- serde という依存を増やしたくない場合、serde を選択制にする。
- これは Cargo.toml より可能である。
- 一般的に、ユーザはオブジェクトが
Copy
であることを期待していない。また Trait の実装者側の視点で言うと、オブジェクトがString
などの non-Copy な型をメンバに持ってしまった場合、Copy
を実装できなくなるため、後方互換性やメンテナンス性が悪化する。これらの点で Trait にCopy
を実装するのはClone
ほど必須ではない。
Trait を参照や Wrapper に対しても実装する
- Trait を実装した際、
&T where T: Trait
や&mut T where T: Trait
,Box<T> where T: Trait
に対しても Trait を適宜実装する。 -
IntoIterator
を&Trait
と&mut Trait
に対して実装することでユーザは気にせず for 文を書ける。 -
Trait
が Wrapper 型 (e.g.Arc<T>
) である場合Deref
を実装する。そうするとユーザは.
を使って内側の型のメソッドを呼ぶことができる。 - 内側の型の参照を簡単に取り出せることを期待する場合、
AsRef
を実装する。またユーザが自ら Wrap を外せることを望む場合From<InnerType>
とInto<InnerType>
を実装する。
柔軟性
- コードを書くと言うことはコードの Restrictions と Promises を定義することである。
- Restrictions とはそのコードを使用する際の制約である。
- Promises とはそのコードが保証する内容である。
- Trait を定義する際には、Restrictions と Promises を慎重に検討しなければならない。
...続く
Discussion