如何将一个 [[T; 4]; 3] 转换为 [T; 12],最佳的方法是什么?

6
据我了解,在内存中,[[T;4];3][T;12]具有相同的布局。如何在这些类型之间转换值?我能将一个引用转换成另一个引用吗?我能避免复制所有元素吗?需要使用unsafe吗?
2个回答

9
是的,您可以将对[[T; 4]; 3]的引用转换为对[T; 12]的引用,但只能使用不安全代码,使用mem::transmute。最好将其包装在一个函数中,以便分配适当的生命周期给结果引用,否则transmute会使得可能获得比引用应该具有的更长的生命周期的引用。
fn convert<'a>(a: &'a [[u8; 4]; 3]) -> &'a [u8; 12] {
    unsafe { std::mem::transmute(a) }
}

这可以通过生命周期省略规则缩短:
fn convert(a: &[[u8; 4]; 3]) -> &[u8; 12] {
    unsafe { std::mem::transmute(a) }
}

尽管在处理不安全的代码时,如果您更喜欢更明确的版本,我会理解!

1
只是澄清一下:这是否会对“引用本身”进行逐位复制,而不是数组的内容?我不确定。 - Simon Whitehead
3
如果你转化一个数组的 _引用(reference)_,那么只有引用会被复制。如果你直接转化 整个数组(就像你在回答中所做的那样),那么整个数组都会被复制。然而,编译器可能会根据你对值的操作以及代码的编译方式(调试或发布)来省略复制。 - Francis Gagné
没问题,我还是会留下我的答案,作为将来参考的依据。谢谢你的澄清 :) - Simon Whitehead

3

免责声明:我对Rust的低级方面并不是很擅长,不知道在低级Rust中什么被认为是“良好的实践”。这里给出的建议可能不是好主意。但我还是把它们放在这里,因为......它们有效。

你可以转换它们。问题是它会产生一份副本,文档说这相当于一个memcpy调用。这不是你想要的,但它在这里:

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

您的另一个选择 是使用原始指针

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

这并不是特别好。
指针也可以是相同类型的指针或可变指针(因为&mut T可以转换为*mut T),上面的代码同样适用(标记为可变的a)。
let c = &mut a as *mut [[u8; 4]; 3];

我想知道这是否是一个XY问题。也许您处理数据的方式可以改变,不需要这个功能?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接