Edited at

Rust combineでand_then()を使うとき(訂正版)


はじめに

Rust combineでand_then()を使うとき」という記事を書いたのですが、結構間違っている部分があったので訂正版です。

まず、and_thenを使う場合はeasy_parseが必須と書いたのですが、これは嘘でした。

parseが返すエラーcombine::error::ParseErrorstd::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が結構遅いことに気づいたからでした。

折角なのでparseeasy_parseの速度比較もしてあります。

Rust combineのparse vs easy_parse