はじめに
「Rust combineでand_then()を使うとき」という記事を書いたのですが、結構間違っている部分があったので訂正版です。
まず、and_then
を使う場合はeasy_parse
が必須と書いたのですが、これは嘘でした。
parse
が返すエラーcombine::error::ParseError
がstd::error::Error
を格納できない、と思っていたのですが、実際には
ParseError::StreamError::other()
で格納することができました。
ただしFrom
は実装されていないので、自分で変換する必要があります。
これを踏まえて、parse
でもeasy_parse
でも使える再利用可能なパーサは以下のようになります。
and_then
を使った再利用可能なパーサ(訂正版)
fn num<I>() -> impl Parser<Input = I, Output = i32>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
many1(digit())
.and_then(|x: String| {
x.parse::<i32>().map_err(|x| {
<I::Error as combine::error::ParseError<char, I::Range, I::Position>>::StreamError::other(x)
})
})
}
map_err
で自力で変換することで、前回記事のtrait boundに必要だったFrom
はいらなくなっています。
また、途中に出てくる長いエラー型はtype annotation含めあちこちで使うのでaliasしておくと便利かもしれません。
type AndThenError<I> = <<I as StreamOnce>::Error as ParseError<
<I as StreamOnce>::Item,
<I as StreamOnce>::Range,
<I as StreamOnce>::Position,
>>::StreamError;
fn num<I>() -> impl Parser<Input = I, Output = i32>
where
I: Stream<Item = char>,
I::Error: ParseError<I::Item, I::Range, I::Position>,
{
many1(digit())
.and_then::<_, _, AndThenError<I>, _>(|x: String| {
x.parse::<i32>().map_err(|x| {
AndThenError::<I>::other(x)
})
})
}
単にエラーメッセージを渡したい場合は
AndThenError::<I>::message_static_message("error!!!")
が使えます。
おまけ
前回の結果で一段落したつもりだったところを再調査したのはeasy_parse
が結構遅いことに気づいたからでした。
折角なのでparse
とeasy_parse
の速度比較もしてあります。