Rust dojo第18回を開催した

第18回です。

前回はこちら。

kdnakt.hatenablog.com

 

 

[第18回の様子]

2021/11/17に第18回を開催した。

 

内容としてはRust By Example 日本語版の「6.2. TryFromおよびTryInto」、「6.3. Stringとの型変換」に取り組んだ。

 

参加者はちょっと増えて、全部で8人。

 

今日はSlackでの関連発言が活発だった。

f:id:kidani_a:20211118224137p:plain

f:id:kidani_a:20211118224020p:plain

f:id:kidani_a:20211118223913p:plain

 

[学んだこと]

use std::convert::TryFrom;

#[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)) // 偶数だったらOkでEvenNumber型を返す
        } else {
            Err(()) // 奇数の場合はErrを返す
        }
    }
}

fn main() {
    // TryFrom
    assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8)));
    assert_eq!(EvenNumber::try_from(5), Err(()));
}
  • Intoを使うとFromが呼び出されるのと同様、TryIntoを使うとTryFromの実装が呼び出される
let result: Result<EvenNumber, ()> = 8i32.try_into(); // 先ほど使ったtry_from()が呼び出される
assert_eq!(result, Ok(EvenNumber(8)));
  • Result<EvenNumber, ()>を何度も書くのがつらい場合は、前回やったエイリアスを使うと良さそう
type Optional<T> = Result<T, ()>;
let result: Optional<EvenNumber> = 8i32.try_into();
  • 6.3. Stringとの型変換
  • Stringへの変換にはToStringトレイトを実装する必要がある
  • ただし、fmt::Displayトレイトを実装すると自動的にToStringトレイトも実装されるのでこちらがおすすめ
use std::fmt;

struct Circle {
    radius: i32
}

impl fmt::Display for Circle {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Circle of radius {}", self.radius)
    }
}

fn main() {
    let circle = Circle { radius: 6 };
    println!("{}", circle);
    // Circle of radius 6
    println!("{}", circle.to_string());
    // Circle of radius 6
}
  • 文字列から数値への変換はparse()関数を利用する
// 変数側で型を指定する:i32型がFromStrトレイトを実装しているため利用可能
let parsed: i32 = "5".parse().unwrap();

// turbofish構文を利用して型を指定する
let turbo_parsed = "10".parse::<i32>().unwrap();
  • ここで登場したunwrap()はResult<T, S>から結果を取り出して(unwrapして)くれる:かわりに、以下のように?を使うと、結果を取り出しつつ例外を呼び出しもとに伝えてくれる
type ParseIntResult<T> = Result<T, std::num::ParseIntError>;

fn sum_strs(m: &str, n: &str) -> ParseIntResult<i32> {
    let parsed: i32 = m.parse()?;
    let turbo_parsed = n.parse::<i32>()?;
    Ok(parsed + turbo_parsed)
}

fn main() {
    let sum = sum_strs("5", "10");
    println!("Sum: {:?}", sum); // Sum: Ok(15)

    let sum = sum_strs("a", "10");
    println!("Sum: {:?}", sum); // Sum: Err(ParseIntError { kind: InvalidDigit })
}
  • 書き方がスッキリしている。これを使いこなせるようになりたい...
  • ちなみに、?演算子を使わずにmatchで愚直に書くと次のようになる。冗長...
fn sum_strs2(m: &str, n: &str) -> ParseIntResult<i32> {
    let result_parsed: ParseIntResult<i32> = m.parse();
    match result_parsed {
        Result::Ok(parsed) => {
            let result_turbo_parsed = n.parse::<i32>();
            match result_turbo_parsed {
                Result::Ok(turbo_parsed) => Ok(parsed + turbo_parsed),
                Result::Err(e) => Err(e),
            }
        },
        Result::Err(e) => Err(e),
    }
}

 

[まとめ]

モブプログラミングスタイルでRust dojoを開催した。

Resultと?演算子を使いこなせるようになりたい...!

 

今週のプルリクエストはこちら。

github.com