🦀

100日後にRustをちょっと知ってる人になる: [Day 47]型変換ためのトレイト: From / Into

2022/10/15に公開

Day 47 のテーマ

Day 46 では、Rust のに関して見てみました。どのような型システムをとっているのか、また型を明示的に変換するキャストの仕組みなどについて確認を行いました。

ところでこの型の変換に関してですが、std::convert という型を変換するトレイトを提供しているモジュールがあります。

提供しているトレイト毎に目的が異なった変換を実施します:

  • AsRef: 参照から参照への変換
  • AsMut: MutableからMutableへの変換
  • From: 値から値への変換
  • Into: 現在のクレートの外側の型への値から値への変換

この中から FromInto について使い方を確認しておきます。

From トレイト

From トレイトは次の様に定義されています。

pub trait From<T> {
    fn from(T) -> Self;
}

この From トレイトは、ある型に対して、別の型からその型を作る方法を定義できるようにするものです。

例えば、str に対して String を作る場合は次のようになります。

let my_str = "my str";
let my_string = String::from(my_str);

また、i32 から自作の Number 型 を作る場合は次のようになります。

struct Number {
    value: i32,
}

impl From<i32> for Number {
    fn from(item: i32) -> Self {
        Number { value: item }
    }
}
let num = Number::from(30);

つまり、このような実装ということになります。

impl From<変換元> for 変換先 {
    fn from(from: 変換元) -> 変換先 {
    }
}

Into トレイト

Into トレイトは次のように定義されています。

pub trait Into<T> {
    fn into(self) -> T;
}

この Into は、From トレイトの逆の関係のトレイトになっています。
自作の型に From トレイトが実装されている場合、Into は必要に応じてそれを呼び出します。

struct Number {
    value: i32,
}

impl From<i32> for Number {
    fn from(item: i32) -> Self {
        Number { value: item }
    }
}

ここで Into を使用すると以下のように定義が行なえます。

let num: Number = 30.into();

つまり、同じ処理を以下の 2 種類で表わせるということになります。

  • From トレイト
    • let num = Number::from(30);
  • Into トレイト
    • let num: Number = 30.into();

Day 47 のまとめ

FromInto が対になっているという説明をよく見かけるのですが、それだけを真に受けると変換の方向を誤解してしまうと思いました。
FromInto により T 型 と U 型の間で相互に変換可能になるのではありません。(はじめ僕もそうなのかと思っていました…)
変換の方向は同じで、T 型 → U 型という変換であることを間違えないように覚えておかないといけないと思います。
どちらも同じひょうげんだとすると、個人的には、From を実装していれば事足りるかなと思いました。

Into ならではの使いどころはどこなんでしょう…とちょっと考えたい)

GitHubで編集を提案

Discussion