Cursor<Vec<u8>>::read() doesn't read to buffer

I cannot figure out what I'm doing wrong here. I want to read from a Cursor<Vec<u8>> but nothing is ever read out:

use std::io::{Cursor, Read, Write};

fn main() {
    let mut cursor: Cursor<Vec<u8>> = Cursor::new(Vec::new());

    let write_size = cursor.write(b"Hello\n").expect("couldn't write.");

    //let mut buf: Vec<u8> = vec![0; 6];
    let mut buf = [0u8; 6];

    dbg!(write_size);
    dbg!(String::from_utf8_lossy(cursor.get_ref()));
    dbg!(String::from_utf8_lossy(&buf));

    let read_size = cursor.read(&mut buf).expect("unable to read.");

    dbg!(read_size);
    dbg!(String::from_utf8_lossy(cursor.get_ref()));
    dbg!(String::from_utf8_lossy(&buf));
}

Output

[src\main.rs:10] write_size = 6
[src\main.rs:11] String::from_utf8_lossy(cursor.get_ref()) = "Hello\n"
[src\main.rs:12] String::from_utf8_lossy(&buf) = "\0\0\0\0\0\0"
[src\main.rs:16] read_size = 0
[src\main.rs:17] String::from_utf8_lossy(cursor.get_ref()) = "Hello\n"
[src\main.rs:18] String::from_utf8_lossy(&buf) = "\0\0\0\0\0\0"

This is a simplification of a much larger system where the Cursor is a member of a struct and is being used as a buffer.

Am I doing something wrong or just misunderstanding what this is supposed to do?

You need to seek to the start of the cursor before reading. You also need to call read_exact() if you want to be sure that the whole buffer is filled.

Playground.

1 Like

Your problem is that the Cursor has a a position which moves forward after you write.

When you write Hello\n, the cursor points to the end of the string you just wrote. To fix this, simply seek back to the start.

use std::io::{Cursor, Read, Seek, SeekFrom, Write};

fn main() {
    let mut cursor: Cursor<Vec<u8>> = Cursor::new(Vec::new());

    let write_size = cursor.write(b"Hello\n").expect("couldn't write.");

    //let mut buf: Vec<u8> = vec![0; 6];
    let mut buf = [0u8; 6];

    dbg!(write_size);
    dbg!(String::from_utf8_lossy(cursor.get_ref()));
    dbg!(String::from_utf8_lossy(&buf));

    cursor.seek(SeekFrom::Start(0)); // this part seeks back to the start!
    let read_size = cursor.read(&mut buf).expect("unable to read.");

    dbg!(read_size);
    dbg!(String::from_utf8_lossy(cursor.get_ref()));
    dbg!(String::from_utf8_lossy(&buf));
}
2 Likes

A std::io::Cursor has two main elements - the underlying buffer (a Vec<u8> in this case) and a u64 which points at the "current location" in that buffer.

Think of it like a file.

When you write, you are writing to the end of the buffer, and because you are still at the end of the buffer after the write, there is nothing left to read.

1 Like

Thank you! That was the part I was missing. Thanks!

Note that although the accepted answer doesn't correct that mistake, you still should be using read_exact() instead of read(), or you should at least not ignore the return value of read(). From the docs:

Pull some bytes from this source into the specified buffer, returning how many bytes were read
[…]
It is not an error if the returned value n is smaller than the buffer size, even when the reader is not at the end of the stream yet. This may happen for example because fewer bytes are actually available right now (e. g. being close to end-of-file) or because read() was interrupted by a signal.

As this trait is safe to implement, callers in unsafe code cannot rely on n <= buf.len() for safety. Extra care needs to be taken when unsafe functions are used to access the read bytes. Callers have to ensure that no unchecked out-of-bounds accesses are possible even if n > buf.len().

No guarantees are provided about the contents of buf when this function is called, so implementations cannot rely on any property of the contents of buf being true. It is recommended that implementations only write data to buf instead of reading its contents.

Just as a follow-up: This is what I ended up going with, because the cursor didn't really provide all the functionality I needed since I needed to permanently consume the data being read. I just use Take on a Vec<u8> and then set the Vec<u8> to a slice without the read portion.

use std::io::{Cursor, Read, Seek, SeekFrom, Write};

fn main() {
    let mut cursor: Vec<u8> = Vec::new();

    let write_size = cursor.write(b"Hello\n").expect("couldn't write");
    //internal.extend_from_slice(format!("{x}").as_bytes());

    let mut buf: Vec<u8> = vec![0; 6];
    let mut buf = [0u8; 6];

    dbg!(write_size);
    dbg!(String::from_utf8_lossy(&cursor));
    dbg!(String::from_utf8_lossy(&buf));

    let Ok(read_size) = cursor.take(buf.len() as u64).read(&mut buf) else {
        eprintln!("Unable to read");
        return;
    };
    cursor = cursor[read_size..].into();

    dbg!(read_size);
    dbg!(String::from_utf8_lossy(&cursor));
    dbg!(String::from_utf8_lossy(&buf));
}

Output

[src\main.rs:12] write_size = 6
[src\main.rs:13] String::from_utf8_lossy(&cursor) = "Hello\n"
[src\main.rs:14] String::from_utf8_lossy(&buf) = "\0\0\0\0\0\0"
[src\main.rs:22] read_size = 6
[src\main.rs:23] String::from_utf8_lossy(&cursor) = ""
[src\main.rs:24] String::from_utf8_lossy(&buf) = "Hello\n"

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.