In a rather low level part of a project of mine, a function receives a mutable slice of primitive data (&mut [u32]
in this case). This data should be written to a writer in little endian.
Now, this alone wouldn't be a problem, but all of this has to be fast. I measured my application and identified this as one of the critical paths. In particular, if the endianness doesn't need to be changed (since we're already on a little endian system), there shouldn't be any overhead.
This is my code (Playground):
use std::{io, mem, slice};
fn write_data(mut w: impl io::Write, data: &mut [u32]) -> Result<(), io::Error> {
adjust_endianness(data);
// Is this safe?
let bytes = unsafe {
let len = data.len() * mem::size_of::<u32>();
let ptr = data.as_ptr() as *const u8;
slice::from_raw_parts(ptr, len)
};
w.write_all(bytes)
}
fn adjust_endianness(_: &mut [u32]) {
// implementation omitted
}
adjust_endianness
changes the endianness in place (which is fine, since a wrong-endian u32
is garbage, but still a valid u32
).
This code works, but the critical question is: Is this safe? In particular, at some point, data
and bytes
both exist, being one mutable and one immutable slice to the same data. That sounds very bad, right?
On the other hand, I can do this:
let bytes = &data[..];
That way, I also have those two slices. The difference is just that data
is now borrowed.
Is my code safe or does it exhibit UB? Why? If it's not safe, how to safely do what I want to do?
unsafe
Rust, it seems an answer could demonstrate unsafety but could not demonstrate safety. – Interlacebytes
is derived fromdata
-- it's not that different from just doinglet foo = &*data
, which also gives you a&
slice from a&mut
slice. Whether one can prove that is sound according to Rust's execution model, I'm not sure. – Nonchalant&[u32]
into a&[u8]
. – Scruplewrite_all
only once. With as much data as possible. – Shrumlet bytes = &data[..];
before casting to a raw pointer. In particular, your cast to a raw pointer drops the lifetime, and allows your&mut [u32]
to alias with an&[u8]
, and that seems like UB to me. It would be nice to get @RalfJung's take on this. – Manvellet bytes = &data[..];
before the raw pointer cast feels like it puts this pretty firmly in "that's safe" territory. – Manvel