I have some code which parses files, very similar to the following:
use std::io::Cursor;
use std::io::Result;
use std::io::Read;
fn main() {
let v1 = [1, 2, 3, 4, 5];
let mut c1 = Cursor::new(v1); // *
println!("{:?}", read_one(&mut c1)); // 1
println!("{:?}", read_three(&mut c1)); // 2, 3, 4
println!("{:?}", read_one(&mut c1)); // 5
println!("{:?}", read_one(&mut c1)); // eof
}
fn read_one<R: Read>(r: &mut R) -> Result<u8> {
let mut buf = [0; 1];
r.read_exact(&mut buf)?;
Ok(buf[0])
}
fn read_three<R: Read>(r: &mut R) -> Result<[u8; 3]> {
let mut buf = [0; 3];
r.read_exact(&mut buf)?;
Ok(buf)
}
Here, read_one
and read_three
take a Read
trait, get some bytes off of it, and return the result. I can then read the bytes piece by piece.
I'd like to convert this code to using an iterator instead. That is, have the *
line be
let mut i1 = v1.iter();
Trying this, I ran into problems where I couldn't find functions to easily extract a number of items (analogous to read_exact
) or otherwise partially process an iterator. It seems most of the iterator functions are designed to consume the content entirely.
Is the iterator just a bad tool for this or am I using it wrong? If the latter, how do I convert this code to use iterators instead?
You can make your own trait for that
use std::io::{Error, ErrorKind, Result};
trait ReadExact {
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()>;
}
impl<T> ReadExact for T
where
T: Iterator<Item = u8>,
{
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
for p in buf {
if let Some(b) = self.next() {
*p = b;
} else {
return Err(Error::new(ErrorKind::UnexpectedEof, "Iterator exhausted"));
}
}
Ok(())
}
}
Here is the explanation of why you can't collect into an array
If you still want to use an iterator you can change return type to Vec and call r.take(n).collect();
This, however, will be a little bit slower.
You can write almost the same thing with iterators. The problem you'll run into is that arrays are difficult to work with, because they don't (as of December 2017) implement FromIterator
. As user1541501 suggested , the simplest solution is to collect()
into a Vec
instead:
fn main() {
let v1 = vec![1, 2, 3, 4, 5];
let mut c1 = v1.into_iter();
println!("{:?}", read_one(&mut c1)); // 1
println!("{:?}", read_three(&mut c1)); // 2, 3, 4
println!("{:?}", read_one(&mut c1)); // 5
println!("{:?}", read_one(&mut c1)); // eof
}
fn read_one<I: Iterator<Item = u8>>(i: &mut I) -> Option<u8> {
i.next()
}
fn read_three<I: Iterator<Item = u8>>(i: &mut I) -> Result<Vec<u8>, Vec<u8>> {
let buf: Vec<u8> = i.take(3).collect();
if buf.len() == 3 {
Ok(buf)
} else {
Err(buf)
}
}
Arrays also don't implement IntoIterator
, so I turned v1
into a Vec<u8>
to call v1.into_iter()
. You could write v1.iter().cloned()
instead.
The other significant change I made was to make read_one
return Option
instead of io::Result
, and make read_three
return Result<Vec<u8>, Vec<u8>>
. That's because iterators can only fail by running out of data. (If read_three
fails, the Err
variant contains the collected elements.)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.