Как создать объект в памяти, который может использоваться как Reader или Writer в Rust?
Мне нужен полностью встроенный объект, который я могу передать BufReader
и BufWriter
. Что-то вроде Python StringIO
. Я хочу писать и читать с такого объекта, используя методы, обычно используемые с File
s.
Есть ли способ сделать это, используя стандартную библиотеку?
Ответы
Ответ 1
На самом деле есть способ. Встреча Cursor<T>
!
В документации вы можете увидеть, что существуют следующие иммы:
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>
Из этого вы можете видеть, что вы можете использовать тип Cursor<Vec<u8>>
как обычный файл, потому что для этого типа реализованы Read
, Write
и Seek
!
Маленький пример (Игровая площадка):
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);
Для более полезного примера проверьте документацию, связанную выше.
Ответ 2
Если вы хотите использовать BufReader
с встроенной памятью String
, вы можете использовать метод 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()));
}
Отпечатает read_buff got Potato!
. Нет необходимости использовать курсор для этого случая.
Чтобы использовать встроенную память String
с BufWriter
, вы можете использовать метод as_mut_vec
. К сожалению, это unsafe
, и я не нашел другого пути. Мне не нравится подход Cursor
, поскольку он потребляет вектор, и я еще не нашел способ использовать Cursor
вместе с 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);
}
}
Ответ 3
Вам больше не нужен Cursor
.
объект, который я могу дать BufReader
и BufWriter
BufReader
требует значения, которое реализует Read
:
impl<R: Read> BufReader<R> {
pub fn new(inner: R) -> BufReader<R>
}
BufWriter
требует значения, которое реализует Write
:
impl<W: Write> BufWriter<W> {
pub fn new(inner: W) -> BufWriter<W> {}
}
Если вы просмотрите разработчиков Read
вы найдете impl<'a> Read for &'a [u8]
.
Если вы просмотрите исполнителей Write
, вы найдете 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);
}
Запись в Vec
всегда будет добавлена к концу. Мы также берем фрагмент для Vec
который мы можем обновить. Каждое чтение c
будет продвигать срез дальше и дальше до тех пор, пока оно не станет пустым.
Основные отличия от Cursor
:
- Не удается найти данные, поэтому вы не можете легко перечитать данные
- Невозможно писать нигде, кроме конца