Как создать объект в памяти, который может использоваться как 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:

  • Не удается найти данные, поэтому вы не можете легко перечитать данные
  • Невозможно писать нигде, кроме конца