値型から値型への型変換を行う二つのトレイトについて。

基礎知識

From<T>

T から型 Self への変換をサポートするトレイト。

唯一の関数 form(T) -> Self を持つ。

Into<T>

Self から型 T への変換をサポートするトレイト。

唯一の関数 into(self) -> T を持つ。

サンプル

以下では、i32 から f32 への変換を frominto のそれぞれで確認している。


fn main() {
    let src: i32 = 1;
    let dst: f64 = f64::from(src);
    assert_eq!(dst, 1.0);

    let src: i32 = 1;
    let dst: f64 = src.into();
    assert_eq!(dst, 1.0);
}

補足: 使い分け

殆どの場合、from でも into でも同じ処理を書ける。ちなみに筆者は、コードを短くするため、型推論先が始めからあれば into、なければ from を選んでいる。


fn main() {
    let src = 1;

    f(f64::from(src));
    f(src.into());

    g(f64::from(src));
    g::<f64>(src.into());
}

fn f(_: f64) {}
fn g<T>(_: T) {}

重要な性質

表現の重複

以下の実装はどちらも、TypeXTypeY 変換を表す。

そのため、片方があれば、もう片方もあると期待される。

自動的な実装

From 側のみを実装すれば、Into 側は自動的に実装される。

これは以下のようなブランケット実装の定義で実現されている。


impl<T, U> Into<U> for T where U: From<T> { /* ... */ }

恒等変換

あらゆる型に対して、恒等変換 (何もしない変換) が存在する。

これは以下のようなブランケット実装の定義で実現されている。


impl<T> From<T> for T { /* ... */ }

動的型には未対応

FromInto はトレイトオブジェクトの変換には向かない。
なぜなら、これらはどちらも Sized の実装が前提になっている。

ちなみに、トレイトオブジェクトを扱う場合、IntoIterator が参考になる。
(その逆変換 FromIteratorSized を実装している点に注意。)

変換の制限

変換には守られるべき制限がいくつかある。

以下は From の API ドキュメントからの抜粋。

From による変換の定義

前述の通り、変換の定義は From 側で行う。


fn main() {
    let val = MyType::from(42);
    assert_eq!(val, 42.into());
}

#[derive(Debug, Eq, PartialEq)]
struct MyType(i32);

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

Into による引数型の拡張

引数型を Into<SomeType> とすると、SomeType 以外の型にも対応できる。


fn main() {
    show_float(42.0);
    show_float(42);
}

fn show_float<T: Into<f64>>(x: T) {
    println!("{}", x.into())
}