Rust: использование двух структурных полей u8 как u16 - PullRequest
2 голосов
/ 10 июля 2020

Для эмулятора Gameboy у вас есть два поля u8 для регистров A и F, но иногда к ним можно обращаться как к AF, объединенному регистру u16.

В C похоже, что вы можете это сделать примерно так:

struct {
    union {
        struct {
            unsigned char f;
            unsigned char a;
        };
        unsigned short af;
    };
};

(Взято из здесь )

Есть ли в Rust способ, в идеале без unsafe, для доступа к двум u8s как registers.a / registers.f, но также можно использовать их как u16 registers.af?

1 Ответ

4 голосов
/ 10 июля 2020

Я могу дать вам несколько способов сделать это. Первый - простой небезопасный аналог, но без шаблона, второй - безопасный, но явный.

  1. Объединения в ржавчине очень похожи, так что вы можете перевести это так:
#[repr(C)]
struct Inner {
    f: u8,
    a: u8,
}

#[repr(C)]
union S {
    inner: Inner,
    af: u16,
}

// Usage:

// Putting data is safe:
let s = S { af: 12345 };
// but retrieving is not:
let a = unsafe { s.inner.a };
Или, в качестве альтернативы, вы можете вручную выполнить все явные приведения, заключенные в структуру:
#[repr(transparent)]
// This is optional actually but allows a chaining,
// you may remove these derives and change method
// signatures to `&self` and `&mut self`.
#[derive(Clone, Copy)]
struct T(u16);

impl T {
    pub fn from_af(af: u16) -> Self {
        Self(af)
    }
    
    pub fn from_a_f(a: u8, f: u8) -> Self {
        Self::from_af(u16::from_le_bytes([a, f]))
    }

    pub fn af(self) -> u16 {
        self.0
    }

    pub fn f(self) -> u8 {
        self.0.to_le_bytes()[0]
    }

    pub fn set_f(self, f: u8) -> Self {
        Self::from_a_f(self.a(), f)
    }

    pub fn a(self) -> u8 {
        self.0.to_le_bytes()[1]
    }

    pub fn set_a(self, a: u8) -> Self {
        Self::from_a_f(a, self.f())
    }
}

// Usage:

let t = T::from_af(12345);
let a = t.a();

let new_af = t.set_a(12).set_f(t.f() + 1).af();
...