Open9

rust トレイト

katayama8000katayama8000

トレイトを実装

トレイトとは、別の言語では interface と表されることが多い

trait Pay {
    fn pay(self, price: i32) -> Self;
}

関数の引数と戻り値の型が定義されている
このトレイトを struct に実装する

struct Money {
    amount: i32,
}

trait Pay {
    fn pay(self, price: i32) -> Self;
}

impl Pay for Money {
    fn pay(self, price: i32) -> Self {
        Money {
            amount: self.amount - price,
        }
    }
}

typescriptだとimplements

katayama8000katayama8000

トレイトを用意しなくても、struct に関数果たせる

struct Dollar {
    amount: i32,
}

impl Dollar {
    fn draw(self, val: i32) -> Self {
        Dollar {
            amount: self.amount - val,
        }
    }
}
katayama8000katayama8000

ジェネリクス

struct Money<T> {
    amount: T,
}
 let money: Money<i32>  = Money { amount: 10000 };
// or
 let money: Money<i64> = Money::<i64> { amount: 10000 };

何も書かないと、型推論してくれる

ジェネリックなメソッド

struct Money<T> {
    amount: T,
}

impl<T> Money<T> {
    fn refer(self) -> Self {
        Money {
            amount: self.amount,
        }
    }
}

impl<T> と書くと、ジェネリックな形になる

 let money: Money<i32> = Money { amount: 10000 };
 let amount: Money<i32> = money.refer();

特定の方のみに実装する

impl Money<i32> {
    fn pay(self, val: i32) -> Self {
        Money {
            amount: self.amount - val,
        }
    }
}

// OK
let money: Money<i32> = Money { amount: 10000 };
let amount: Money<i32> = money.pay(1000);

// NG
let money = Money::<i64> { amount: 10000 };
// i64 には pay method が生えていない
katayama8000katayama8000

ちなみに、上記のようにインスタンス化しなくても

let money = Money::<i32> { amount: 10000 };
let money = Money::pay(money, 1000);

のように使うこともできる

katayama8000katayama8000

例えば、

impl ToString for str {
    #[inline]
    fn to_string(&self) -> String {
        String::from(self)
    }
}

"some_str".to_string()
katayama8000katayama8000

From トレイト

前段はこのくらいにして、rust にはトレイトがそれはもうたくさん用意されている
Fromトレイトもそのうちの一つ

pub trait From<T>: Sized {
    /// Converts to this type from the input type.
    #[rustc_diagnostic_item = "from_fn"]
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn from(value: T) -> Self;
}
impl From<i32> for Money<i32> {
    fn from(s: i32) -> Self {
        Money { amount: s }
    }
}

let money = Money::from(10);

上は Money<i32> に i32 を引数にとって、 Money<i32> を返す関数 from を実装している
当然 引数が f32 だとエラー
Money<i32> に f32 を引数にとって、 Money<i32> を返す関数 from は

impl From<f32> for Money<i32> {
    fn from(s: f32) -> Self {
        Money { amount: s as i32 }
    }
}
  let money = Money::from(10.0);

複数定義すると、コンパイラがわからなくなるので、指定が必要(kawaii)

impl From<i32> for Money<i32> {
    fn from(s: i32) -> Self {
        Money { amount: s }
    }
}
impl From<i32> for Money<f32> {
    fn from(s: i32) -> Self {
        Money { amount: s as f32 }
    }
}
let money = Money::<i32>::from(100);
katayama8000katayama8000

trait にジェネリクス

trait Deal<T> {
    fn refer(val: T) -> Self;
}

impl Deal<i32> for Money<i32> {
    fn refer(val: i32) -> Self {
        Money { amount: val }
    }
}
katayama8000katayama8000

Into トレイト

pub trait Into<T>: Sized {
    /// Converts this type into the (usually inferred) input type.
    #[must_use]
    #[stable(feature = "rust1", since = "1.0.0")]
    fn into(self) -> T;
}

やっと本題
into は from を実装したら、自動で実装される

let money1: Money<i32> = 10.into();
// money1 is Money { amount: 10 }
let money2: i32 = 10.into();
// money2 is 10

into は↑のような感じで使う

理解し難いポイントは戻り値を、上の例のように型で値が変わるのである

前述されているように、実態ではなく、構造体から、作ってみる

let ret1 = Into::<Money<i32>>::into(10);
// ret1 is Money { amount: 10 }

into は self を食って、T(Money<i32>)を返しているだけ
なので、

let ret2 = Into::<i32>::into(10);
// ret2 is 10

こうすると、形によって値が変わる

let ret2: i32 = Into::into(10);
// ret2 is 10

ジェネリクスで書かなくても、同じ結果である

前述したように複数の可能性があると、コンパイラは型を決められない