As I understand it, a [[T; 4]; 3]
and a [T; 12]
have the same layout in memory. What is the best way to convert a value between these types? Can I convert a reference to one into a reference to the other? Can I avoid copying all the elements? Do I need unsafe
?
Yes, you can convert a reference to a [[T; 4]; 3]
into a reference to a [T; 12]
, but only with unsafe code, using mem::transmute
. It's best to wrap this in a function so that the resulting reference is assigned the proper lifetime, as otherwise transmute
would make it possible to obtain a reference with a larger lifetime than the reference should have.
fn convert<'a>(a: &'a [[u8; 4]; 3]) -> &'a [u8; 12] {
unsafe { std::mem::transmute(a) }
}
This can be shortened thanks to the lifetime elision rules:
fn convert(a: &[[u8; 4]; 3]) -> &[u8; 12] {
unsafe { std::mem::transmute(a) }
}
Though when dealing with unsafe code, I'd understand if you preferred the more explicit version!
Disclaimer: I am not really great with Rust's low-level side of things yet, I don't know what is considered "good practice" in low-level Rust. The advice given here may not be good ideas. I'm putting them here though because... well, they work.
You could transmute them. The problem is that it'll be a copy, the documentation says it's the equivalent of a memcpy
call. This isn't what you wanted, but here it is anyway:
fn main() {
let a: [[u8; 4]; 3] = [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]];
let b: [u8; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
println!("a: {:?}", a);
println!("b: {:?}", b);
let c = unsafe { std::mem::transmute::<[[u8; 4]; 3], [u8; 12]>(a) };
println!("c: {:?}", c);
}
Your other option is to work with a raw pointer:
fn main() {
let a: [[u8; 4]; 3] = [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]];
let b: [u8; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
println!("a: {:?}", a);
println!("b: {:?}", b);
let c = &a as *const _ as *const [u8; 12];
// Or it can be this: let c = &mut a as *mut [[u8; 4]; 3];
for i in 0..12 {
let p = c as *const u8;
let v = unsafe { *p.offset(i) };
println!("{}", v);
}
}
which isn't particularly great either.
The pointer can also be a pointer or mutable pointer to the same type (since &mut T
can be cast to *mut T
), and the above code works exactly the same (with a
marked mutable):
let c = &mut a as *mut [[u8; 4]; 3];
I do wonder whether this is a bit of an XY-Problem though. Maybe the way you're working with your data can be altered to not require this?
© 2022 - 2024 — McMap. All rights reserved.