🦁

Rust 基本文法 - From、TryFrom -

2024/03/21に公開

はじめに

Rust を使っていく中で、From、TryFrom を実装する機会がありました。
初見ではなんのこっちゃ理解できなかったので、同じく躓く方もいらっしゃるのではないでしょうか。

コードは Rust Playground でも試すことができますので、もし宜しければ手を動かしてみてください。
https://play.rust-lang.org/

From について

Fromでは、Aの型をBの型に変換することが可能です。

以下の例のように struct Numberimpl From<T> for Tを実装します。
※Rust 公式ドキュメントからの引用です。

#[derive(Debug)]
struct Number {
    value: i32,
}

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

impl From<T>の T部分にはベースとなる(変換対象の)型を宣言します。
for T の T部分には変換先の型を指定します。

呼び出し元では、以下のように記述します。

fn main() {
    let numa = Number::from(30);
    let numb: Number = 30.into();
    println!("My number is {:?}", numa);
    println!("My number is {:?}", numb);
}

fromもしくはintoを使って呼び出すことが可能です。
こうすることで、i32型で渡された値が、自作したNumber型に変換されます。
impl From<T> for Tを実装しましたが、呼び出し元の呼び出しパターンは2種類あることがわかります。

TryFrom について

TryFrom は From と近いですが、返り値として Result<S> を返します。
Rusult を返す、ということはつまり、Error が起こりうるということになります。

From では変換処理で Error となることは想定しませんでしたが、TryFrom では Error となることを想定した実装をする必要があります。
どういうことかというと、ドキュメントの内容を見つつ深堀りします。

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

impl TryFrom<i32> for EvenNumber {
    type Error = ();

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        if value % 2 == 0 {
            Ok(EvenNumber(value))
        } else {
            Err(())
        }
    }
}

書き方自体は、Fromと同じです。
impl TryFrom<T> for Tのように<T>には変換元、変換先の型を指定します。
try_fromという関数を実装していますが、先ほど述べたように、Resultが返るように実装されています。
引数で受け付けた、i32型の値が、2で割り切れる場合のみ、EvenNumber(i32)に変換するという処理になります。
つまり条件付きの型変換処理ということです。

呼び出し元では、以下のように記述します。

    let numa = EvenNumber::try_from(8);
    // Result なので match で処理する
    match numa {
        Ok(num) => println!("Number is {:?}", num),
        Err(_) => println!("Number is not even"),
    }

    let numb: Result<EvenNumber, ()> = 8.try_into();
    // Result なので match で処理する
    match numb {
        Ok(num) => println!("Number is {:?}", num),
        Err(_) => println!("Number is not even"),
    }

try_fromもしくはtry_intoを使って呼び出すことが可能です。
先ほど紹介した、From の呼び出し方とほとんど変わりません。
Resultなのでエラーハンドリングをしましょう、といった違いくらいです。

おわりに

後だしですが、Rust のドキュメントに FromTryFromはしっかりと書かれています。
実際、Rust を使って開発していると、実装するケースは必ず出てくると思います。

▼ドキュメント
https://doc.rust-jp.rs/rust-by-example-ja/conversion/from_into.html
https://doc.rust-jp.rs/rust-by-example-ja/conversion/try_from_try_into.html

ではではー!

コラボスタイル Developers

Discussion