¿Cómo crear un objeto en memoria que se pueda usar como Lector, Escritor o Buscar en Rust?
in-memory (3)
Necesito un objeto completamente en memoria que puedo dar a
BufReader
y
BufWriter
.
Algo como el
StringIO
de Python.
Quiero escribir y leer desde un objeto de este tipo utilizando métodos que normalmente se usan con
File
s.
¿Hay alguna manera de hacer esto usando la biblioteca estándar?
De hecho hay un camino.
¡Conoce al
Cursor<T>
!
En la documentación puede ver que hay las siguientes implicaciones:
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>
A partir de esto, puede ver que
puede usar el tipo
Cursor<Vec<u8>>
como un archivo ordinario
, porque ¡
Read
,
Write
y
Seek
están implementadas para ese tipo!
Pequeño ejemplo ( 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);
Para un ejemplo más útil, consulte la documentación vinculada anteriormente.
No necesitas un
Cursor
mayor parte del tiempo
.
objeto que puedo dar a
BufReader
yBufWriter
BufReader
requiere un valor que implemente
Read
:
impl<R: Read> BufReader<R> {
pub fn new(inner: R) -> BufReader<R>
}
BufWriter
requiere un valor que implemente
Write
:
impl<W: Write> BufWriter<W> {
pub fn new(inner: W) -> BufWriter<W> {}
}
Si ve los implementadores de
Read
, encontrará
impl<''a> Read for &''a [u8]
.
Si ve los implementadores de
Write
, encontrará
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);
}
Escribir a un
Vec
siempre se agregará al final.
También tomamos una porción del
Vec
que podemos actualizar.
Cada lectura de
c
avanzará el corte más y más hasta que esté vacío.
Las principales diferencias con el
Cursor
:
- No puede buscar los datos, por lo que no puede volver a leerlos fácilmente
- No se puede escribir en ninguna parte excepto al final
Si desea usar
BufReader
con una
String
en memoria, puede usar el método
as_bytes()
:
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
fn read_buff<R: Read>(mut buffer: BufReader<R>) {
let mut data = String::new();
let _ = buffer.read_line(&mut data);
println!("read_buff got {}", data);
}
fn main() {
read_buff(BufReader::new("Potato!".as_bytes()));
}
Esto imprime
read_buff got Potato!
.
No hay necesidad de usar un cursor para este caso.
Para usar una
String
en memoria con
BufWriter
, puede usar el método
as_mut_vec
.
Lamentablemente no es
unsafe
y no he encontrado ninguna otra manera.
No me gusta el enfoque del
Cursor
ya que consume el vector y todavía no he encontrado una manera de usar el
Cursor
junto con
BufWriter
.
use std::io::BufWriter;
use std::io::Write;
pub fn write_something<W: Write>(mut buf: BufWriter<W>) {
buf.write("potato".as_bytes());
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{BufWriter};
#[test]
fn testing_bufwriter_and_string() {
let mut s = String::new();
write_something(unsafe { BufWriter::new(s.as_mut_vec()) });
assert_eq!("potato", &s);
}
}