Perl日記

日々の知ったことのメモなどです。Perlは最近やってないです。

Rust のトレイトとかトレイトオブジェクトとかサブトレイトとかジェネリクスのときのトレイトのやつのメモ

いろいろ試したメモ。なんとかぼんやりと理解できてきた。


トレイト:共通の振る舞いを定義する - The Rust Programming Language 日本語版

↑このページを見ながらいろいろ確認。

  • トレイトを関数の引数に指定する
    • 1つ目の書き方
    • 2つ目の書き方
  • 2つのトレイトを持つインスタンスを関数の引数に指定する
    • 1つ目の書き方
    • 2つ目の書き方
  • 関数がトレイトを実装した固定の型を返すとき
  • 関数がトレイトを実装した固定ではない型を返すとき
  • トレイトを実装した型を Enum にくるんで使うとき
// トレイト1
trait Summary {
    fn summarize(&self) -> String;
}

// トレイト2
trait Count {
    fn count(&self) -> u32;
}

// 構造体1
struct Foo {
    f: String,
    c: u32,
}

// 構造体1にトレイト1を実装
impl Summary for Foo {
    fn summarize(&self) -> String {
        format!("f: {}", self.f)
    }
}

// 構造体1にトレイト2を実装
impl Count for Foo {
    fn count(&self) -> u32 {
        self.c
    }
}

// 構造体2
struct Bar {
    b: String,
}

// 構造体2にトレイト1を実装
impl Summary for Bar {
    fn summarize(&self) -> String {
        format!("b: {}", self.b)
    }
}

// 構造体2にトレイト2を実装しない
// impl Count for Bar {}

// 引数にトレイト(1つ目の書き方)
fn notify1(item: &impl Summary) {
    println!("notify1 {}", item.summarize());
}

// 引数にトレイト(2つ目の書き方)
fn notify2<T: Summary>(item: &T) {
    println!("notify2 {}", item.summarize());
}

// 引数に2つのトレイト(1つ目の書き方)
fn notify3<T: Summary + Count>(item: &T) {
    println!("notify3 {}", item.summarize());
}

// 引数に2つのトレイト(2つ目の書き方)
fn notify4<T>(item: &T)
where
    T: Summary + Count,
{
    println!("notify4 {}", item.summarize());
}

fn notify5(item: &Box<dyn Summary>) {
    println!("notify5 {}", item.summarize());
}

// トレイト1のインスタンスを返す
fn returns_impl_summary(c: u32) -> impl Summary {
    // 返す構造体は固定
    Foo {
        f: String::from("returns_impl_summary"),
        c: c,
    }
}

// トレイト1かトレイト2のインスタンスを返す
// 返す構造体は固定ではないので、Box で包んでトレイトオブジェクトにする
fn returns_box_dyn_summary(c: u32) -> Box<dyn Summary> {
    if c > 0 {
        Box::new(Foo {
            f: String::from("returns_box_dyn_summary"),
            c: c,
        })
    } else {
        Box::new(Bar {
            b: String::from("returns_box_dyn_summary"),
        })
    }
}

enum MyStruct {
    T1(Foo),
    T2(Bar),
}

// Enum なら Box を使わずにいける
fn returns_enum_summary(c: u32) -> MyStruct {
    if c > 0 {
        MyStruct::T1(Foo {
            f: String::from("returns_enum_summary"),
            c: c,
        })
    } else {
        MyStruct::T2(Bar {
            b: String::from("returns_enum_summary"),
        })
    }
}

fn main() {
    let f1 = Foo {
        f: String::from("foo"),
        c: 1,
    };
    notify1(&f1);
    notify2(&f1);
    notify3(&f1);
    notify4(&f1);

    let f2 = returns_impl_summary(2);
    notify1(&f2);
    notify2(&f2);
    // Foo インスタンスで、Count トレイトも実装しているが、コンパイラには直接伝わらない
    // notify3(&f2);  the trait `Count` is not implemented for `impl Summary`
    // notify4(&f2);  the trait `Count` is not implemented for `impl Summary`

    let f3 = returns_impl_summary(3);
    notify1(&f3);
    notify2(&f3);
    // Foo インスタンスで、Count トレイトも実装しているが、コンパイラには直接伝わらない
    // notify3(&f3);  the trait `Count` is not implemented for `impl Summary`
    // notify4(&f3);  the trait `Count` is not implemented for `impl Summary`

    let f4 = returns_box_dyn_summary(4);
    // Box<dyn Summary> 型なので、Summary や Count は実装していないとみなされてしまうようだ
    // notify1(&f4);  the trait `Summary` is not implemented for `Box<dyn Summary>`
    // notify2(&f4);  the trait `Summary` is not implemented for `Box<dyn Summary>`
    // notify3(&f4);  the trait `Summary` is not implemented for `Box<dyn Summary>` the trait `Count` is not implemented for `impl Summary`
    // notify4(&f4);  the trait `Summary` is not implemented for `Box<dyn Summary>` the trait `Count` is not implemented for `impl Summary`
    // そのまんまの型の引数なら渡せる
    notify5(&f4);

    // Enum なら Foo 構造体であることが伝えられるので、notify 1~4 が使えるようだ
    if let MyStruct::T1(f5) = returns_enum_summary(5) {
        notify1(&f5);
        notify2(&f5);
        notify3(&f5);
        notify4(&f5);
    }

    // ---------------------------------------------------------------
    let b = Bar {
        b: String::from("bar"),
    };
    notify1(&b);
    notify2(&b);
    // Count は実装していないのでコンパイルエラーになる
    // notify3(&b);
    // notify4(&b);
}
notify1 f: foo
notify2 f: foo
notify3 f: foo
notify4 f: foo
notify1 f: returns_impl_summary
notify2 f: returns_impl_summary
notify1 f: returns_impl_summary
notify2 f: returns_impl_summary
notify5 f: returns_box_dyn_summary
notify1 f: returns_enum_summary
notify2 f: returns_enum_summary
notify3 f: returns_enum_summary
notify4 f: returns_enum_summary
notify1 b: bar
notify2 b: bar


トレイト:共通の振る舞いを定義する - The Rust Programming Language 日本語版

  • ジェネリクスな型のメソッドを実装するとき、トレイトを実装していることを条件にできる
    • トレイト境界
use std::fmt::Display;

struct Pair<T> {
    x: T,
    y: T,
}

// ジェネリクスな型のメソッド定義
impl<T> Pair<T> {
    fn new(x: T, y: T) -> Self {
        Self { x, y }
    }
}

// Display トレイトと PartialOrd トレイトを実装している T のときのみ使用可能なメソッド
impl<T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("The largest member is x = {}", self.x);
        } else {
            println!("The largest member is y = {}", self.y);
        }
    }
}

// Foo 構造体は、Clone トレイトは実装するが、Display も PartialOrd も実装しない
#[derive(Clone)]
struct Foo;

trait Bar {}
impl Bar for Foo {}

impl<T: Bar> Pair<T> {
    fn yay(&self) {
        println!("yay");
    }
}

fn main() {
    let pair1 = Pair::<u32>::new(10, 20);
    let pair2 = Pair::<i16>::new(6, 5);
    pair1.cmp_display();
    pair2.cmp_display();
    let foo = Foo {};
    let pair3 = Pair::new(foo.clone(), foo.clone());
    // pair3.cmp_display();   method cannot be called on `Pair<Foo>` due to unsatisfied trait bounds
    pair3.yay();
    // pair2.yay();  method cannot be called on `Pair<i16>` due to unsatisfied trait bounds
}
The largest member is y = 20
The largest member is x = 6
yay


高度なトレイト - The Rust Programming Language 日本語版

  • サブトレイト
    • トレイトを実装するときに、別のトレイトの実装も強制できるやつ
trait A1 {}     // 普通のトレイト
trait A2: A1 {} // A2 の実装には A1 の実装も必要という意味

// OK
struct S1;
impl A1 for S1 {}
impl A2 for S1 {}

// NG
struct S2;
// A2 を実装しているのに A1 を実装していないと、コンパイルエラーとなる
impl A2 for S2 {}