I have a byte buffer of unknown size, and I want to create a local struct variable pointing to the memory of the beginning of the buffer. Following what I'd do in C, I tried a lot of different things in Rust and kept getting errors. This is my latest attempt:
use std::mem::{size_of, transmute};
#[repr(C, packed)]
struct MyStruct {
foo: u16,
bar: u8,
}
fn main() {
let v: Vec<u8> = vec![1, 2, 3];
let buffer = v.as_slice();
let s: MyStruct = unsafe { transmute(buffer[..size_of::<MyStruct>()]) };
}
I get an error:
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> src/main.rs:12:42
|
12 | let s: MyStruct = unsafe { transmute(buffer[..size_of::<MyStruct>()]) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
There are a few things that transmute is really useful for. Turning a pointer into a function pointer. This is not portable to machines where function pointers and data pointers have different sizes. Extending a lifetime, or shortening an invariant lifetime. This is advanced, very unsafe Rust!
Otherwise the function is fairly safe since it prevents buffer over-run since the output is read-only, fixed number of bytes, and its lifetime is bound to the input. If you wanted a version that returned a &mut [u8], that would be quite dangerous since modifying could easily create inconsistent/corrupt data.
Turning an &str into an & [u8]: Turning a Vec<&T> into a Vec<Option<&T>>. To transmute the inner type of the contents of a container, you must make sure to not violate any of the container’s invariants. For Vec, this means that both the size and alignment of the inner types have to match.
buffer provides safe, write-only and generics-free byte buffers that can be used without initializing them first. The main trait of this library is Buffer that represents a type that can contain uninitialized bytes (such as Vec, ArrayVec, etc.) and can safely be read into (e.g. using ReadBuffer ).
If you don't want to copy the data to the struct but instead leave it in place, you can use slice::align_to
. This creates a &MyStruct
instead:
#[repr(C, packed)]
#[derive(Debug, Copy, Clone)]
struct MyStruct {
foo: u16,
bar: u8,
}
fn main() {
let v = vec![1u8, 2, 3];
// I copied this code from Stack Overflow
// without understanding why this case is safe.
let (head, body, _tail) = unsafe { v.align_to::<MyStruct>() };
assert!(head.is_empty(), "Data was not aligned");
let my_struct = &body[0];
println!("{:?}", my_struct);
}
Here, it's safe to use align_to
to transmute some bytes to MyStruct
because we've used repr(C, packed)
and all of the types in MyStruct
can be any arbitrary bytes.
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With