What is the best way to convert [[T; 4]; 3] in [T; 12]?

As I understand it, [[T; 4]; 3] [[T; 4]; 3] [[T; 4]; 3] and [T; 12] [T; 12] have the same layout in memory. What is the best way to convert a value between these types? Can I convert a link to one into a link to another? Can I avoid copying all elements? Do I need unsafe ?

+6
source share
2 answers

Yes, you can convert the link to [[T; 4]; 3] [[T; 4]; 3] [[T; 4]; 3] in reference to [T; 12] [T; 12] , but only with unsafe code using mem::transmute . It is best to wrap this in a function so that the resulting link is assigned the proper lifetime, since otherwise transmute would allow a link with a longer lifetime than the link should have.

 fn convert<'a>(a: &'a [[u8; 4]; 3]) -> &'a [u8; 12] { unsafe { std::mem::transmute(a) } } 

This can be reduced thanks to life permitting rules:

 fn convert(a: &[[u8; 4]; 3]) -> &[u8; 12] { unsafe { std::mem::transmute(a) } } 

Although, when dealing with unsafe code, I would understand if you prefer a more explicit version!

+9
source

Disclaimer: I am not very well versed in Rust at a low level, but I do not know what is considered “good practice” in the early stages of Rust. The recommendations given here may not be good ideas. I put them here, though ... well, they work.

You can transform them . The problem is that it will be a copy, the documentation says that this is the equivalent of calling memcpy . This is not what you wanted, but it doesn’t matter here:

 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); } 

Another 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 is also not particularly large.

The indicator may also be a pointer or a pointer to a variable the same type (as &mut T can be discarded before *mut T ), and the above code works exactly the same way (with a marked variability):

 let c = &mut a as *mut [[u8; 4]; 3]; 

I really wonder if this is a bit of an XY issue. Maybe the way you work with your data can be changed so as not to require it?

+3
source

Source: https://habr.com/ru/post/1013114/


All Articles