【问题标题】:Is it legal to cast a struct to an array?将结构转换为数组是否合法?
【发布时间】:2020-09-26 02:57:30
【问题描述】:

考虑以下几点:

// 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) 结构可以像数组一样重新解释,是否总是安全的?为什么?

【问题讨论】:

    标签: rust slice unsafe memory-layout object-model


    【解决方案1】:

    是的,它既安全又便携,除了非常大的T(在下面修复)。 std::slice::from_raw_parts 文档的安全部分中列出的所有要点都不是这里的问题:

    • 数据指针对mem::size_of::&lt;T&gt;() * 4有效,为S&lt;T&gt;的大小,并正确对齐。

      • 所有项目都在同一个分配对象中,因为它们在同一个结构中。
      • 指针不为空,因为它是从安全的&amp;self 参数转换而来的,而且它是正确对齐的,因为S&lt;T&gt; 具有(至少)T 的对齐方式。
    • data参数肯定指向4个连续初始化的Ts,因为S在你的结构中被标记为#[repr(C)]其中is defined such that,不会引入填充。 (repr(Rust) 不做这样的保证)。

    • 引用的内存在引用的生命周期内不会发生突变,这是由借用检查器保证的。

    • 切片的总大小不得大于isize::MAX。代码不检查这一点,所以它在技术上是一个安全漏洞。可以肯定的是,在unsafe 之前添加一个检查到as_slice

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

      检查通常会被优化出来。

    【讨论】:

    • 所以也保证字段之间不会有填充?
    • @Nazinho 不,repr(c) 不会删除填充。它以与C 相同的方式填充结构。如果要删除填充,可以使用repr(packed)doc.rust-lang.org/nomicon/other-reprs.html
    • 标准 Rust array 中的元素打包与 repr(C) 结构中的字段相同(大小向上舍入到下一个对齐倍数)
    • "它以与 C 相同的方式填充结构。" 请注意,C 标准没有指定填充,但留下了实现细节。它确实指定数组中没有内部填充。
    • @ex nihilo OTOH Rust 说 stride >= size 用于数组
    猜你喜欢
    • 2015-06-22
    • 2023-04-08
    • 2014-11-13
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 2021-12-01
    相关资源
    最近更新 更多