I have the following API:
fn read_complex_struct(reader: impl Read) -> ComplexStruct {
let mut tmp_buffer = Box::new([0u8; 16384]);
...
}
I need tmp_buffer
not only to amortize syscall cost, but actually need to be able to perform random memory access in it for efficient string search. So I can't use BufRead
trait.
I want users to be able to provide their own (reused) buffer from the outside. So I do this:
fn read_complex_struct(reader: impl Read, tmp_buffer: &mut [u8])
-> ComplexStruct { .. }
However, now I have exposed the user to a bunch of potential borrowing problems, even if they don't want to deal with "optimizing" buffers. So I iterate further:
fn read_complex_struct(reader: impl Read, tmp_buffer: impl AsMut<[u8]>)
-> ComplexStruct { .. }
Then people can send in Box::new([0u8; 16384])
and it will not require them to hold a buffer elsewhere. This matters because the actual real-world example of read_complex_struct
actually looks closer to this:
impl Reader<R: Read, Buffer: AsMut<[u8]>> {
fn new_with_buffer(reader: R, tmp_buffer: Buffer) -> Self { .. }
fn new(reader: R) -> Self { Self::new_with_buffer(reader, Box::new(...)) }
}
anyway, ability to pass owned data into our read_complex_struct
is desirable to make lifetimes simpler when one doesn't care about performance. I found that forcing users to pass in borrowed, non-static data quickly forces them to deal with self-referential structs (aka their own structs becoming self-referential).
However, there's another problem. read_complex_struct
's correctness relies on tmp_buffer
not resizing itself. In other words, tmp_buffer.as_mut().len()
must never change. Unfortunately, AsMut<[u8]>
provides few static guarantees around that.
So, my final attempt:
fn read_complex_struct<const BUF_LEN: usize>(
reader: impl Read,
tmp_buffer: impl AsMut<[u8; BUF_LEN]>
) -> ComplexStruct { .. }
surely if the length of the buffer is known statically at compile-time again, it cannot change? But it would actually be nice to not only let users choose their buffer size, but allow them to do so at runtime. For as long as they don't change their mind halfway through.
What is the most common and idiomatic solution here? Build my own trait like AsRef<[u8]>
that comes with a tighter contract? Is there such a thing already?