なんか考えてることとか

変な人が主にプログラミング関連で考えていることをまとめる。

【補足】RustのトレイトはMixinか?など。

Rustのトレイトはトレイトではないの続編的な物。というか補足。

RustのトレイトはMixinか?

  • 2022/02/12
    • 以下の記事にて書き直しました。「RustのトレイトはMixInではない」項をご覧ください。

opaupafz2.hatenablog.com

トレイトとMixinは非常に似た概念であるため、しばしば比較されることがある。そのため、前に書いた記事を読んで共感を得た人たちは疑問に思ったことだろう。「RustのトレイトはMixinなのか?」

率直に言うと、RustのトレイトはMixinではない

まずMixinで有名なのはRubymoduleによるMixinだが、Pythonでも実現可能なので今回はPythonでMixinを実現してみる。

class AMixIn:
    '''
    MixinA
    '''
    def method(self):
        print(self._label, 'MixinA')

class BMixIn:
    '''
    MixinB
    '''
    def method(self):
        print(self._label, 'MixinB')

class ImplClass(AMixIn, BMixIn):
    '''
    MixinA, MixinBをMixin
    '''
    def __init__(self):
        self._label = 'Mixin:'

if __name__ == '__main__':
    # ImplClassのインスタンスを生成
    instance = ImplClass()
    
    # methodを実行
    instance.method()

これを実行してみると・・・

Mixin: MixinA

エラーにならず、実行することができた。そして、methodメソッドはAMixInmethodにオーバーライドされていることがわかる。

実はMixinでは多重Mixinした際に名前衝突が生じるとオーバーライドされることが前提となっており、Rustの場合、オーバーライドされず別個のメソッドとして扱われるため、この辺りがMixinとは異なる。

なお、PythonRubyは多重継承した際ある法則に則って最終的に一つのメソッドにオーバーライドする仕組みになっているので、名前衝突した際エラーになってしまうプログラミング言語も存在するが、結局は最終的に一つのメソッドにオーバーライドするので、そのような仕組みが存在しないRustのトレイトはMixinであるとは言えない。

じゃあ結局Rustのトレイトは何だよ?という話になるが、今のところ有力候補として挙がっているのは「型クラス」説である。
型クラスとはその名のとおり型のクラスなのだが、実装の仕方や継承の仕組みがそっくりなので今のところ型クラスではないかという方向で調査を続けている。
そのために今現在型クラスと呼ばれる概念が存在するHaskellというプログラミング言語について学習しているので、型クラスをちゃんと理解したときに、まとめようと思っている。

菱形継承問題はどうやって回避しているのか?

実はもうすでに答えは出ている。
Rustで実装されるメソッドはすべて別個のメソッドとして実装されるため、これにより菱形継承問題も回避している。

Rustにオーバーライドは存在するのか?

する(直球)。トレイトを実装した際にオーバーライドすることが可能

2022/02/12追記
Rustのトレイトではメソッドのオーバーライドはできません。詳細は以下をご覧ください。
opaupafz2.hatenablog.com

// トレイト
pub trait Overridden {
    // オーバーライドされるメソッド
    fn method(&self);
}

// 構造体
pub struct ImplStruct;

// トレイトを構造体に実装
impl Overridden for ImplStruct {
    // メソッドをオーバーライド
    fn method(&self) {
        println!("Overrode.");
    }
}

fn main() {
    // ImplStructのインスタンスを生成
    let instance = ImplStruct;
    
    // methodを実行
    instance.method();
}

Rust Playground

Overrode.

このオーバーライドの仕様で個人的によく考えられているなと感じたのは、構造体にトレイトを実装しているimplスコープ内ではオーバーライドするメソッドしか書けないということ。新しくメソッドを定義する場合、構造体のimplスコープ内に書く必要がある

/* トレイト, 構造体定義部分は省略 */

// トレイトを構造体に実装
// ※オーバーライドするメソッドしか記述できない!
impl Overridden for ImplStruct {
    // メソッドをオーバーライド
    fn method(&self) {
        println!("Overrode.");
    }
    
    // 新しくメソッドを定義してしまうとエラーになる
    // pub fn method_new(&self) {
    //     println!("This is method in ImplStruct.");
    // }
}

// 構造体にメソッドを定義
// ※新しいメソッドはここでしか記述できない!
impl ImplStruct {
    pub fn method_new(&self) {
        println!("This is method in ImplStruct.");
    }
}

fn main() {
    // ImplStructのインスタンスを生成
    let instance = ImplStruct;
    
    // methodを実行
    instance.method();      // オーバーライドしたメソッド
    instance.method_new();  // 新しく定義したメソッド
}

Rust Playground

Overrode.
This is method in ImplStruct.

この仕様により、「このメソッドはトレイトにより実装されたのか?そうではないのか?」を区別することが容易になる