Rustで文字列・ファイル、両方からbyteを読みたいだけなのに
バイト配列をイテレータで読み込みたい
Rustでプログラミング言語を作っている。基本的には、ソースコードはファイルを読み込むのだが、テストのために固定の文字列をソースコードとして読ませたくなることが多い。そういうわけで、字句解析器(Lexer)に両方を受け付けられるインターフェースを作ろうとしたのだが、これが意外と面倒なことに気づいたので書く。
文字列もファイルからのストリームもイテレータなのに
&str
からイテレータを取り出すときは、bytes
メソッドを使えばIteratorが取れる。一方、ファイルではstd::io::BufReader
にもbytes
メソッドがある。これらはRead
トレイトのメソッドなので、「じゃあ、Read
型を引数で受け取ればいいのかな」と安易な気持ちで書いてみたら、全然ビルドが通らない。よーく見ると、当然だった。
まず、文字列&str
の場合。
let s = "Please iterate me!"; let mut bytes = s.bytes(); while let Some(b) = bytes.next() { print!({:?}, b); }
そして、ファイルから読み込む場合。
let f = File::open("_.txt").unwrap(); let mut bytes = BufReader::new(f).bytes(); while let Some(Ok(b)) = bytes.next() { print!({:?}, b); }
おわかりいただけただろうか……next
メソッドの返す型が違うのである。str::bytes()
はIterator<Item=u8>
を返し、BufReader::bytes()
はIterator<Item=::std::io::Result<u8>>
を返す。ファイルは間にstd::io::Result
が挟まっているのである。だから当然これらを共通の引数にしてメソッドを定義することはできない。
しょうがない
というわけで、普通にどちらでもいけるようにがりがりifで書き直しました。そのまんま、何の工夫もしていないので見せるほどのコードでもない。しかし、もうちょっと何とかならんかなあ。
補足
Iterator<Item=::std::io::Result<u8>>
は、Iterator<Item=Result<u8>>
とは書けない。std::result::Result
だと思われてしまうので。ただ、Iterator<Item=std::io::Result<u8>>
とも書けない。うまくパースできないようなので、Iterator<Item=::std::io::Result<u8>>
と書く必要があります。これって::
を省いて書けるようになったりするんかな。まあともかく、そんな感じです。