Rustで2つのReadを連結したReadを作る
hr.icon
やりたいこと
これができるとバイト列を読み込み過ぎたときに元のReadとくっつけてる実装ができてpeekしたいときにも役に立つ。
使い方の目標
code:rs
fn main() -> std::io::Result<()> {
// 2つのファイルを用意する
let f1 = std::fs::File::open("hello.txt")?;
let f2 = std::fs::File::open("world.txt")?;
// f1とf2の2つのファイルをくっつける
let mut combined = combine_reads(f1, f2);
// 二つ連続したものを読み出す
let mut str = String::new();
combined.read_to_string(&mut str);
println!("{:?}", str);
Ok(())
}
簡単に確認するため.read_to_string()を使っているがf1とf2のread()は必要なときに呼ばれるように作る(一種のストリーミング)。
Readを連結するための実装
以下で実現可能。
code:rs
use std::io::Read;
struct CombinedRead<R1, R2> {
r1: R1,
r2: R2,
reading_r1: bool,
}
impl<R1: Read, R2: Read> Read for CombinedRead<R1, R2> {
fn read(&mut self, buf: &mut u8) -> std::io::Result<usize> { if self.reading_r1 {
let size = self.r1.read(buf)?;
if size == 0 {
self.reading_r1 = false;
return self.r2.read(buf);
}
return Ok(size)
}
return self.r2.read(buf);
}
}
fn combine_reads<R1: Read, R2: Read>(r1: R1, r2: R2) -> CombinedRead<R1, R2> {
CombinedRead {
r1,
r2,
reading_r1: true,
}
}
型が異なるReadでも連結できる
例えば以下のように&[u8]とFileのように異なるReadであっても連結できる。
code:rs
let f1 = std::fs::File::open("hello.txt")?;
let mut combined = combine_reads(data1, f1);
let mut str = String::new();
combined.read_to_string(&mut str);
println!("{:?}", str);
これのおかげでbufで読み過ぎたバイト列を戻せる。
peekぽいできる
peekぽくしたいときは、read()の戻り値のsizeで部分を切り出すようにすることに注意。