スマートポインタ (Deref
や DerefMut
トレイトを実装する型) まわりの慣習について。
スマートポインタでは、メソッドとして実装できる処理であっても、通常の関連関数として定義されている事が多い (言い換えると、スマートポインタでは self
や &self
などの引数の使用が少ない)。
これにはコードを分かりやすくする狙いがある。
ここで、ドット演算子からのメソッドの呼出において、スマートポインタには Deref
型強制がある。つまり、指定されたメソッド名は、スマートポインタ自身のメソッド、そして参照先のメソッド、その両方から探される。
そのため、コードリーディング時はそのメソッドがどちらに属するのか、そのつど調べないといけない。また、コードライティング時はメソッド名が衝突している場合、参照先のメソッドを呼ぶには、まず手動で参照を外さないといけない。一方、スマートポインタ側の処理の多くがそもそもメソッドでなければ、この問題はいくらか緩和される。
関数をメソッドにすべきかどうかは、その処理対象による。具体的には、処理対象がスマートポインタ自身ならば通常の関連関数、処理対象がスマートポインタの参照先ならばメソッドとする。そのため、Box<dyn Any>
の downcast<T>(self)
等はメソッド形式である。
この方法には限界もあり、スマートポインタが実装するトレイトのメソッドまでは呼出方法を強制できない。これには、Box<T, A>
に実装される Clone::clone(&self)
等がある。Box
等と関数形式で呼ぶ事もできるが、あくまで任意であり一般的でもない。
std
クレートの多くのスマートポインタも、この慣習に従っている。
Box<T, A>::into_raw(b: Box<T, A>)
Box<T, A>::leak<'a>(b: Box<T, A>)
Rc<T, A>::downgrade(this: &Rc<T, A>)
Rc<T, A>::strong_count(this: &Rc<T, A>)
Rc<T, A>::weak_count(this: &Rc<T, A>)
なお、Rc<T, A>::downgrade(this: &Rc<T, A>)
の逆操作 Weak<T, A>
はメソッド形式である。この違いは Rc
と Weak
の Deref
の実装有無に由来する。
以下では、MyPtr
型の dup
関数は _dup
関数より好ましい実装方法である。
use std::ops::Deref;
fn main() {
let p1 = MyPtr::new(42);
let p2 = MyPtr::dup(&p1);
assert_eq!(*p1, *p2);
}
struct MyPtr<T> {
value: Box<T>
}
impl<T: Clone> MyPtr<T> {
fn new(value: T) -> Self {
Self { value: Box::new(value) }
}
fn dup(this: &Self) -> Self {
Self { value: this.value.clone() }
}
fn _dup(&self) -> Self {
Self { value: self.value.clone() }
}
}
impl<T> Deref for MyPtr<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value.as_ref()
}
}