How to write_all to a slice in Rust many times and get output

use std::io::Write;

fn main() {
    let mut buffer: &mut [u8] = &mut [0u8; 10];
    buffer.write_all(&[1u8,2,3]).unwrap();
    buffer.write_all(&[4u8,5]).unwrap();
    println!("{:?}", buffer);
}

this prints [0, 0, 0, 0, 0] because it splits the slice and gives the rest for more writes. Rust Playground

What would be the most elegant way to get the actual output after many writes, and not the remainder slice? I don't want to keep track of how many bytes were written just so I can subslice the buffer output.

Context: microcontrollers don't allow for memory allocation so I pass a worst case buffer slice for write and it's almost never filled to the end.

Put the &mut [u8] in a different variable.

    let mut buffer = [0u8; 10];
    let mut slice = &mut buffer[..];
    slice.write_all(&[1u8, 2, 3]).unwrap();
    slice.write_all(&[4u8, 5]).unwrap();
    println!("{:?}", buffer);
2 Likes

Oh wait, you probably want just the first five bytes.

    let mut buffer = [0u8; 10];
    let mut slice = &mut buffer[..];
    slice.write_all(&[1u8, 2, 3]).unwrap();
    slice.write_all(&[4u8, 5]).unwrap();
    let left = slice.len();
    let written = buffer.len() - left;
    println!("{:?}", &buffer[..written]);
    // Need to write more?  Recreate slice
    let slice = &mut buffer[written..];
2 Likes

I've hid a bit of the problem, here's the full one:

use std::io::Write;

pub struct A{
    pub a: u8,
    pub b: u16
}

impl A {
    pub fn to_bytes(&self, output: &mut &mut[u8]) -> Result<(), std::io::Error> {
        output.write_all(&[self.a])?;
        output.write_all(&self.b.to_be_bytes()[..])?;
        Ok(())
    }
}

fn main() {
    let mut buffer: &mut [u8] = &mut [0u8; 10];
    let a = A{
        a: 7,
        b: 100
    };
    a.to_bytes(&mut buffer);
    println!("{:?}", buffer);
}

the idea was to get the buffer automatically setted to its content only, when doing ˋto_bytesˋ. Unfortunately I cannot save the original slice before writing to it, so I can set it back after writing. It seems the only way would be to set after I call to_bytes, every time, which is a little bit annoying

I don't understand what you mean, sorry. But when I change your code as @quinedot suggested, it does write to the buffer -- see below.

The reason it works is that it is writing via a slice that refers to the buffer. There is only one buffer, and the bytes are written there. The slice is just a pointer and a length, and you can think of it as a window onto the buffer -- it always refers to the unwritten portion of the buffer, and it is advanced to the next unwritten portion by write_all.

use std::io::Write;

pub struct A {
    pub a: u8,
    pub b: u16,
}

impl A {
    pub fn to_bytes(
        &self,
        mut output: &mut [u8],
    ) -> Result<(), std::io::Error> {
        output.write_all(&[self.a])?;
        output.write_all(&self.b.to_be_bytes()[..])?;
        Ok(())
    }
}

fn main() {
    let mut buffer = [0u8; 10];
    let slice = &mut buffer;
    let a = A { a: 7, b: 100 };
    a.to_bytes(slice).unwrap();
    println!("{:?}", buffer);
}

playground


Edit: I think perhaps I know what you mean now. You want to call to_bytes multiple times, appending to the buffer each time -- is that correct? If so, something like the following may be what you want. Note that you cannot access buffer until you're done writing to it, since it is borrowed exclusively by slice during that time.

use std::io::Write;

pub struct A {
    pub a: u8,
    pub b: u16,
}

impl A {
    pub fn to_bytes(&self, output: &mut &mut [u8]) -> Result<(), std::io::Error> {
        output.write_all(&[self.a])?;
        output.write_all(&self.b.to_be_bytes()[..])?;
        Ok(())
    }
}

fn main() {
    let mut buffer = [0u8; 10];
    let mut slice = &mut buffer[..];

    let a = A { a: 7, b: 100 };
    a.to_bytes(&mut slice).unwrap();

    let a2 = A { a: 3, b: 222 };
    a2.to_bytes(&mut slice).unwrap();

    println!("{:?}", buffer);
}

playground

1 Like

Here's one way (that still requires keeping track of how much was written).

You could encapsulate the keeping-track logic too.

1 Like