% 共通の関数呼び出し構文
しばしば、同名の関数が存在する時があります。たとえば、以下のコードでは:
trait Foo {
fn f(&self);
}
trait Bar {
fn f(&self);
}
struct Baz;
impl Foo for Baz {
fn f(&self) { println!("Baz’s impl of Foo"); }
}
impl Bar for Baz {
fn f(&self) { println!("Baz’s impl of Bar"); }
}
let b = Baz;
もしここで、 b.f()
を呼びだそうとすると、以下の様なエラーが発生します:
error: multiple applicable methods in scope [E0034]
b.f();
^~~
note: candidate #1 is defined in an impl of the trait `main::Foo` for the type
`main::Baz`
fn f(&self) { println!("Baz’s impl of Foo"); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: candidate #2 is defined in an impl of the trait `main::Bar` for the type
`main::Baz`
fn f(&self) { println!("Baz’s impl of Bar"); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
このような場合は、どのメソッドを呼び出す必要があるのかについて曖昧性を排除する手段が必要です。 そのようなフィーチャーは 「共通の関数呼び出し構文」と呼ばれ、以下のように書けます:
# trait Foo {
# fn f(&self);
# }
# trait Bar {
# fn f(&self);
# }
# struct Baz;
# impl Foo for Baz {
# fn f(&self) { println!("Baz’s impl of Foo"); }
# }
# impl Bar for Baz {
# fn f(&self) { println!("Baz’s impl of Bar"); }
# }
# let b = Baz;
Foo::f(&b);
Bar::f(&b);
部分的に見ていきましょう。
Foo::
Bar::
まず、呼び出しのこの部分は2つのトレイト Foo
と Bar
の型を表しています。
この部分が、実際にどちらのトレイトのメソッドを呼び出しているのかを指定し、曖昧性を排除している箇所になります。
f(&b)
b.f()
のように メソッド構文 を利用して呼び出した時、Rustは f()
が &self
を引数に取る場合自動的に b
を借用します。
今回の場合は、そのようには呼び出していないので、明示的に &b
を渡してやる必要があります。
すぐ上で説明した、以下のような共通の関数呼び出し構文:
Trait::method(args);
これは短縮形であり、時々必要になる以下の様な展開された形式もあります:
<Type as Trait>::method(args);
<>::
という構文は型のヒントを意味しており、 <>
のなかに型が入ります。
この場合、型は Type as Trait
となり、 Trait
のバージョンの method
が呼ばれる事を期待していることを意味しています。
as Trait
という部分は、曖昧でない場合は省略可能です。山括弧についても同様に省略可能であり、なので先程のさらに短い形になるのです。
長い形式を用いたサンプルコードは以下の通りです:
trait Foo {
fn foo() -> i32;
}
struct Bar;
impl Bar {
fn foo() -> i32 {
20
}
}
impl Foo for Bar {
fn foo() -> i32 {
10
}
}
fn main() {
assert_eq!(10, <Bar as Foo>::foo());
assert_eq!(20, Bar::foo());
}
山括弧構文を用いることでトレイトのメソッドを固有メソッドの代わりに呼び出すことができます。