2021/3/31 更新
高度なトレイト - The Rust Programming Language 日本語版
を知らなかった、これを読んでください
経緯
数時間前に「僕はチンパンジーだからでパーサコンビネータ使うの難しいな〜、LR(1)パーサを手書きするのが一番早いですよ〜」と同僚と会話をしていたid:Krouton です。フルスクラッチでやる縛りを設けてないので今回実装するパーサはパーサジェネレータを使おうかなと考えていました。チンパンジーだから分からん、と逃げていても一生進まないので nom の使い方を調べていたら友人のエントリにヒットして読んでたらFully Qualified Syntaxという知らない概念に出会いました。
該当エントリ内に書いてあるとおり公式を読めば*1終わりっちゃ終わりなのですが、ググって日本語の記事がないのも寂しいのでパーサのモチベが無くならない程度に書きます。
何が嬉しいのか
impl した trait のメソッド名が他の impl と被った時にどの trait のメソッドを呼び出すのか一意に定める事ができます。
解説: selfを取る場合
そんなに難しい概念ではないので公式のソースコードをそのまま持ってきます。
trait Pilot { fn fly(&self); } trait Wizard { fn fly(&self); } struct Human; impl Pilot for Human { fn fly(&self) { println!("This is your captain speaking."); } } impl Wizard for Human { fn fly(&self) { println!("Up!"); } } impl Human { fn fly(&self) { println!("*waving arms furiously*"); } } fn main() { let person = Human; person.fly(); // *waving arms furiously* Pilot::fly(&person); // Pilot trait の fly を呼び出す Wizard::fly(&person); // Wizard trait の fly を呼び出す }
personから Pilot のfly や Wizard の fly が呼び出せます。良かったですね。
余談
struct A; trait B { fn xxx(&self) -> bool; } impl A { fn xxx(&self) {} } impl B for A { fn xxx(&self) -> bool { true } } fn main() { let a = A; // bool じゃないほうが呼ばれるのでコンパイルエラー if a.xxx() { println!("implicit"); } else { println!("explicit"); } }
ちなみにこういう例でも implicit な呼び出しはしてくれないです。それもそうか。
解説: self を取らない場合
selfを取らない場合は
trait Animal { fn baby_name() -> String; } struct Dog; impl Dog { fn baby_name() -> String { String::from("Spot") } } impl Animal for Dog { fn baby_name() -> String { String::from("puppy") } } fn main() { println!("A baby dog is called a {}", Dog::baby_name()); // Dog からどうにかして Animal の baby_name を呼びたいので as を使う println!("A baby dog is called a {}", <Dog as Animal>::baby_name()); }
という風に as を使っていい感じにやります。良かったですね。
そういえばself
を取らない場合も同様に implicit なヤツはやってくれないです。頑張って書きましょう。
所感
impl Person {
}
の例ってtraitをimplしてるわけではないと思うのですがこのケースってなんで呼べばいいんだろう?