Syntax and Semantics
Deref coercions
前回はtraitによる演算子のoverloadを扱いました。
Overloadできる演算子には、間接参照演算子*を実装する、Dereftraitがあります。
このDereftraitはユーザー定義ポインタ型の間接参照に使えますが、
すこし特別な機能も持っています。
まずは普通の使いかたから。
DerefExampleというポインタっぽい型を定義し、Dereftraitをimplします。
間接参照先の型をTargetというassociation typeに指定します。
use std::ops::Deref;
struct DerefExample<T> {
value: T,
}
impl<T> Deref for DerefExample<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
fn main() {
let x = DerefExample { value: 'a' };
assert_eq!('a', *x);
}それではDerefの何が特別かというと、次のようなルールがあります。
型Uに対して、Deref<Target = T>をimplすると、&U型の値は暗黙的に&Tに変換できる。
例を見てみます。
fn foo(s: &str) {
//
}
let owned = "Hello".to_string(); // owned: String
// String implements Deref<Target = str>
// Thus, &String will coerced to &str
foo(&owned);StringはDeref<Target = str>をimplされているので、
&Stringは暗黙的に&strに変換され、foo()に渡されます。
他には、リファレンスカウンタをもつRc<T>型は、Deref<Target = T>をimplされています。
よって、&Rc<T>型の値は暗黙的に&T型にキャストできます。
use std::rc::Rc;
fn foo(s: &str) {
//
}
let owned = "Hello".to_string(); // owned: String
let counted = Rc::new(owned); // counted: Rc<String>
foo(&counted); // &Rc<String> -> &String -> &strこの例では、&Rc<String>から&Stringを経て&strへ、二段階のキャストが起こっています。
Deref and method calls
もう一つ、Derefには特別な機能があります。
それは、メソッドをよぶときは*を省略して、間接参照できるということです。
struct Foo;
impl Foo {
fn foo(&self) { println!("Foo"); }
}
let f = &&Foo;
f.foo();fは&&Foo型ですが、&Fooをとるメソッドが呼べます。
これは、コンパイラが自動的に*を挿入してくれるからです。
*は必要なだけ挿入されるので、(&&&&&&&&&f).foo()みたいなのもコンパイルできます。