How to read (std::io::Read) from a Vec or Slice?
Solution 1
While vectors don't support std::io::Read
, slices do.
There is some confusion here caused by Rust being able to coerce a Vec
into a slice in some situations but not others.
In this case, an explicit coercion to a slice is needed because at the stage coercions are applied, the compiler doesn't know that Vec<u8>
doesn't implement Read
.
The code in the question will work when the vector is coerced into a slice using one of the following methods:
read_4_bytes(&*vec_as_file)
read_4_bytes(&vec_as_file[..])
-
read_4_bytes(vec_as_file.as_slice())
.
Note:
- When asking the question initially, I was taking
&Read
instead ofRead
. This made passing a reference to a slice fail, unless I'd passed in&&*vec_as_file
which I didn't think to do. - Recent versions of rust you can also use
as_slice()
to convert a Vec to a slice. - Thanks to @arete on
#rust
for finding the solution!
Solution 2
std::io::Cursor
std::io::Cursor
is a simple and useful wrapper that implements Read
for Vec<u8>
, so it allows to use vector as a readable entity.
let mut file = Cursor::new(vector);
read_something(&mut file);
And documentation shows how to use Cursor
instead of File
to write unit-tests!
Working example:
use std::io::Cursor;
use std::io::Read;
fn read_something(file: &mut impl Read) {
let _ = file.read(&mut [0; 8]);
}
fn main() {
let vector = vec![1, 2, 3, 4];
let mut file = Cursor::new(vector);
read_something(&mut file);
}
From the documentation about std::io::Cursor
:
Cursors are typically used with in-memory buffers to allow them to implement
Read
and/orWrite
...The standard library implements some I/O traits on various types which are commonly used as a buffer, like
Cursor<Vec<u8>>
andCursor<&[u8]>
.
Slice
The example above works for slices as well. In that case it would look like the following:
read_something(&mut &vector[..]);
Working example:
use std::io::Read;
fn read_something(file: &mut impl Read) {
let _ = file.read(&mut [0; 8]);
}
fn main() {
let vector = vec![1, 2, 3, 4];
read_something(&mut &vector[..]);
}
&mut &vector[..]
is a "mutable reference to a slice" (a reference to a reference to a part of vector), so I just find the explicit option with Cursor
to be more clear and elegant.
Cursor <-> Slice
Even more: if you have a Cursor
that owns a buffer, and you need to emulate, for instance, a part of a "file", you can get a slice
from the Cursor
and pass to the function.
read_something(&mut &file.get_ref()[1..3]);
ideasman42
Updated on June 03, 2022Comments
-
ideasman42 almost 2 years
Vec
s supportstd::io::Write
, so code can be written that takes aFile
orVec
, for example. From the API reference, it looks like neitherVec
nor slices supportstd::io::Read
.Is there a convenient way to achieve this? Does it require writing a wrapper struct?
Here is an example of working code, that reads and writes a file, with a single line commented that should read a vector.
use ::std::io; // Generic IO fn write_4_bytes<W>(mut file: W) -> Result<usize, io::Error> where W: io::Write, { let len = file.write(b"1234")?; Ok(len) } fn read_4_bytes<R>(mut file: R) -> Result<[u8; 4], io::Error> where R: io::Read, { let mut buf: [u8; 4] = [0; 4]; file.read(&mut buf)?; Ok(buf) } // Type specific fn write_read_vec() { let mut vec_as_file: Vec<u8> = Vec::new(); { // Write println!("Writing Vec... {}", write_4_bytes(&mut vec_as_file).unwrap()); } { // Read // println!("Reading File... {:?}", read_4_bytes(&vec_as_file).unwrap()); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // Comment this line above to avoid an error! } } fn write_read_file() { let filepath = "temp.txt"; { // Write let mut file_as_file = ::std::fs::File::create(filepath).expect("open failed"); println!("Writing File... {}", write_4_bytes(&mut file_as_file).unwrap()); } { // Read let mut file_as_file = ::std::fs::File::open(filepath).expect("open failed"); println!("Reading File... {:?}", read_4_bytes(&mut file_as_file).unwrap()); } } fn main() { write_read_vec(); write_read_file(); }
This fails with the error:
error[E0277]: the trait bound `std::vec::Vec<u8>: std::io::Read` is not satisfied --> src/main.rs:29:42 | 29 | println!("Reading File... {:?}", read_4_bytes(&vec_as_file).unwrap()); | ^^^^^^^^^^^^ the trait `std::io::Read` is not implemented for `std::vec::Vec<u8>` | = note: required by `read_4_bytes`
I'd like to write tests for a file format encoder/decoder, without having to write to the file-system.