stringの限界 | Rust by Example
Rust Examplesより
map_errの引数は関数であるのにenumを直接ドーンと代入している箇所があって、どういうことか疑問に思ったので調べてみました。
現象
map_errの引数はFnOnce(E) -> Fであるのに関数の代わりにenumを渡してもきちんと処理されていた。
use std::num::ParseIntError; use std::fmt; type Result<T> = std::result::Result<T, DoubleError>; enum DoubleError { // このエラーに関しては、エラーの詳細を説明するのに追加の情報は必要ない。 EmptyVec, // パースに失敗した場合はParseIntErrorの挙動に追従する。追加の情報を持たせたい // 場合は、この型に、より多くのデータを追加しなくてはならない。 Parse(ParseIntError), } fn double_first(vec: Vec<&str>) -> Result<i32> { vec.first() // エラーを新しい型のものに変換 .ok_or(DoubleError::EmptyVec) .and_then(|s| s.parse::<i32>() // ここでも新しいエラー型へとアップデートさせる。 .map_err(DoubleError::Parse) // <=================ここ .map(|i| 2 * i)) }
検証
ドキュメントの型通りにmap_errを実装するなら
fn double_first(vec: Vec<&str>) -> Result<i32> { vec.first() .ok_or(DoubleError::EmptyVec) .and_then( |s| s.parse::<i32>() .map_err(|e|DoubleError::Parse(e)) .map(|i| 2 * i) ) }
になると思ったし、これで問題なく動きます。
enumのタプル構造体を渡してもどちらでも動くのでもしかしてmap_errだけでなく、他でも動作するのではと思い、以下サンプルを試してみました。
#[derive(Debug)] enum Osaka { Takoyaki(i32), } fn takoyaki<F>(f: F) where F: FnOnce(i32) -> Osaka { println!("{:?}", f(7)); } fn main() { takoyaki(Osaka::Takoyaki); } // 出力:Takoyaki(7)
できてしまいました・・・
検索すると出てきました
列挙型
以下抜粋
列挙型のコンストラクターは関数のようにも使うことができます
試しに意図的にエラーを起こして、エラーメッセージを読んでみます。
fn main() { println!("{}", Osaka::Takoyaki); // エラー } // fn(i32) -> Osaka {Osaka::Takoyaki}` cannot be formatted with the default formatter
すると型が判明し、fn(i32) -> Osakaだということがわかりました。
なんかこんなのもありました。
Rust 1.40.0でtuple構造体とenum variantのコンストラクタがconst fnになる - Qiita
感想
すっ飛ばそうと思ったけど、ちゃんと調べてよかった!スッキリ!