การสร้างฟิลด์ทับซ้อน / ยูเนี่ยนที่ปลอดภัยในโครงสร้าง

Aug 16 2020

ใน C ++ ฉันสามารถสร้างโครงสร้างดังนี้:

union Vector4
{
    struct { float x, y, z, w; };
    float data[4];
};

ดังนั้นฉันจึงสามารถเข้าถึงข้อมูลเป็นฟิลด์หรืออาร์เรย์ที่อยู่ติดกันได้อย่างง่ายดาย อีกวิธีหนึ่งฉันสามารถสร้างตัวชี้ไปยังฟิลด์แรกxและอ่านจากตัวชี้เป็นอาร์เรย์ที่อยู่ติดกัน

ฉันรู้ว่ามี enum แต่ฉันไม่สามารถจ่ายค่าโสหุ้ยเพิ่มเติมได้ ฉันรู้ด้วยว่าฉันสามารถสร้างสหภาพแรงงานใน Rust ได้ แต่พวกเขาต้องการให้ฉันทิ้งรหัสของฉันด้วยunsafeที่ที่ฉันเคยเข้าถึงพวกเขา ซึ่งฉันรู้สึกว่าฉันไม่ควรต้องทำเนื่องจากโค้ดไม่ปลอดภัยเนื่องจากข้อมูลพื้นฐานมักจะแสดงเป็นโฟลต (และฉันต้องการโครงร่าง C #[repr(C)]เพื่อให้คอมไพเลอร์ไม่โยนไปตามลำดับของฟิลด์)

ฉันจะใช้สิ่งนี้ใน Rust ได้อย่างไรเพื่อให้ฉันสามารถเข้าถึงฟิลด์ตามชื่อ แต่ยังสามารถเข้าถึงหน่วยความจำที่ต่อเนื่องกันของโครงสร้างทั้งหมดได้อย่างง่ายดายและปลอดภัย หากเป็นไปไม่ได้มีวิธีใดบ้างที่ฉันจะสามารถแบ่งส่วนโครงสร้างได้อย่างปลอดภัย?

คำตอบ

3 Locke Aug 17 2020 at 00:08

ไม่มีสิ่งที่เรียกว่าสหภาพที่ปลอดภัย โดยส่วนตัวแล้วฉันจะโต้แย้งว่าการส่งสัญญาณระหว่างอาร์เรย์ขนาดคงที่ของประเภทจำนวนเต็มควรได้รับการพิจารณาว่าปลอดภัย แต่ในขณะนี้ไม่มีข้อยกเว้น

ที่ถูกกล่าวว่าที่นี่มีทั้งหมด 100% Vector4ของฉันไม่สหภาพแรงงาน อย่างที่คุณเห็นการDerefทำงานเพื่อซ่อนรหัสที่ไม่ปลอดภัยและทำให้คุณสามารถใช้Vector4เป็นโครงสร้างหรืออาร์เรย์ตามบริบทที่ใช้การแปลงสัญญาณยังไม่เหมาะ แต่ฉันรู้สึกว่าฉันสามารถพิสูจน์ได้ ในกรณีนี้. หากคุณเลือกที่จะทำสิ่งนี้คุณก็อาจต้องการนำไปใช้DerefMutเช่นกัน

use std::ops::Deref;

// I'm not sure if the repr(C) is needed in this case, but I added it just in case.
#[repr(C)]
pub struct Vector4<T> {
    pub x: T,
    pub y: T,
    pub z: T,
    pub w: T,
}

impl<T> Deref for Vector4<T>
where
    T: Copy + Sized,
{
    type Target = [T; 4];

    fn deref(&self) -> &Self::Target {
        use std::mem::transmute;
        unsafe { transmute(self) }
    }
}

pub fn main() {
    let a = Vector4{
        x: 37,
        y: 21,
        z: 83,
        w: 94,
    };

    println!("{:?}", &a[..]);
    // Output: [37, 21, 83, 94]
}