将结构体转换为数组是否合法?

3

请考虑以下内容:

// Just a sequence of adjacent fields of same the type
#[repr(C)]
#[derive(Debug)]
struct S<T> {
    a : T,
    b : T,
    c : T,
    d : T,
}

impl<T : Sized> S<T> {
    fn new(a : T, b : T, c : T, d : T) -> Self {
        Self {
            a,
            b,
            c,
            d,
        }
    }
    // reinterpret it as an array
    fn as_slice(&self) -> &[T] {
        unsafe { std::slice::from_raw_parts(self as *const Self as *const T, 4) }
    }
}

fn main() {
    let s = S::new(1, 2, 3, 4);
    
    let a = s.as_slice();
    
    println!("s :: {:?}\n\
              a :: {:?}", s, a);
}
  • 这段代码是否具有可移植性?
  • 假设一个使用 repr(C) 声明的结构体内部所有字段类型相同,能否安全地将其重新解释为数组?为什么?
1个回答

5

是的,它是安全和便携的,除非 T 非常大(见下文修复)。std::slice::from_raw_parts 文档中列出的安全注意事项在这里都不是问题:

  • The data pointer is valid for mem::size_of::<T>() * 4, which is the size of S<T>, and is properly aligned.

    • All of the items are in the same allocation object, because they are in the same struct.
    • The pointer is not null, because it is a cast from the safe &self parameter, and it is properly aligned, because S<T> has (at least) the alignment of T.
  • The data parameter definitely points to 4 consecutive initialized Ts, because S is marked #[repr(C)] which is defined such that in your struct, no padding would be introduced. (repr(Rust) makes no such guarantee).

  • The memory referenced is not mutated during the lifetime of the reference, which is guaranteed by the borrow checker.

  • The total size of the slice must not be greater than isize::MAX. The code does not check this, so it is technically a safety hole. To be sure, add a check to as_slice, before the unsafe:

    assert!(std::mem::size_of::<S<T>>() <= isize::MAX as _);
    

    The check will normally be optimized out.


1
标准 Rust 数组中元素的打包方式与 repr(C) 结构体中的字段相同(大小向上舍入到对齐的下一个倍数)。 - kmdreko
1
它以与C相同的方式对结构进行填充。需要注意的是,C标准并未规定填充方式,而是将其作为实现细节。但是,它确实指定数组中没有内部填充。 - ad absurdum
@ex nihilo 另一方面,Rust 表示数组的 stride >= size - Nazinho
@Nazinho 这个参考文献的这一部分(https://doc.rust-lang.org/reference/type-layout.html#reprc-structs)说明,在你的情况下,实际上在字段之间没有填充。 - Lukas Kalbertodt
有没有一种标准的方法可以从拥有的结构体转换为拥有的固定大小数组,即如何定义 fn from(s: S) -> [T; 4]?在这种情况下,std::mem::transmute 是否是正确的工具? - Jasha
显示剩余2条评论

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