How to create an in-memory object that can be used as a Reader, Writer, or Seek in Rust?
I need a completely in-memory object that I can give to BufReader
and BufWriter
. Something like Python's StringIO
. I want to write to and read from such an object using methods ordinarily used with File
s.
Is there a way to do this using the standard library?
In fact there is a way: Cursor<T>
!
(please also read Shepmaster's answer on why often it's even easier)
In the documentation you can see that there are the following impls:
impl<T> Seek for Cursor<T> where T: AsRef<[u8]>
impl<T> Read for Cursor<T> where T: AsRef<[u8]>
impl Write for Cursor<Vec<u8>>
impl<T> AsRef<[T]> for Vec<T>
From this you can see that you can use the type Cursor<Vec<u8>>
just as an ordinary file, because Read
, Write
and Seek
are implemented for that type!
Little example (Playground):
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
// Create fake "file"
let mut c = Cursor::new(Vec::new());
// Write into the "file" and seek to the beginning
c.write_all(&[1, 2, 3, 4, 5]).unwrap();
c.seek(SeekFrom::Start(0)).unwrap();
// Read the "file's" contents into a vector
let mut out = Vec::new();
c.read_to_end(&mut out).unwrap();
println!("{:?}", out);
For a more useful example, check the documentation linked above.
You don't need a Cursor
most of the time.
object that I can give to
BufReader
andBufWriter
BufReader
requires a value that implements Read
:
impl<R: Read> BufReader<R> {
pub fn new(inner: R) -> BufReader<R>
}
BufWriter
requires a value that implements Write
:
impl<W: Write> BufWriter<W> {
pub fn new(inner: W) -> BufWriter<W> {}
}
If you view the implementors of Read
you will find impl<'a> Read for &'a [u8]
.
If you view the implementors of Write
, you will find impl Write for Vec<u8>
.
use std::io::{Read, Write};
fn main() {
// Create fake "file"
let mut file = Vec::new();
// Write into the "file"
file.write_all(&[1, 2, 3, 4, 5]).unwrap();
// Read the "file's" contents into a new vector
let mut out = Vec::new();
let mut c = file.as_slice();
c.read_to_end(&mut out).unwrap();
println!("{:?}", out);
}
Writing to a Vec
will always append to the end. We also take a slice to the Vec
that we can update. Each read of c
will advance the slice further and further until it is empty.
The main differences from Cursor
:
- Cannot seek the data, so you cannot easily re-read data
- Cannot write to anywhere but the end