第18回です。
前回はこちら。
[第18回の様子]
2021/11/17に第18回を開催した。
内容としてはRust By Example 日本語版の「6.2. TryFromおよびTryInto」、「6.3. Stringとの型変換」に取り組んだ。
参加者はちょっと増えて、全部で8人。
今日はSlackでの関連発言が活発だった。
[学んだこと]
- 6.2. TryFromおよびTryInto
- From/Intoと似ているが、TryFrom/TryIntoは失敗する可能性のある型変換で利用する
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と?演算子を使いこなせるようになりたい...!
今週のプルリクエストはこちら。