Возможно, я не слежу за всем , что вы говорите, но я думаю, что вам нужно union
.
#[derive(Clone, Copy, Debug, PartialEq)]
enum Type<'ts> {
TVar(usize),
Constructed(&'ts ConstructedType<'ts>),
}
union CompactType<'ts> {
num: usize,
ptr: &'ts ConstructedType<'ts>
}
impl<'ts> From<CompactType<'ts>> for Type<'ts> {
fn from(compact: CompactType<'ts>) -> Type<'ts> {
unsafe {
if compact.num & 1 == 1 {
Type::TVar(compact.num >> 1)
} else {
Type::Constructed(compact.ptr)
}
}
}
}
Обратите внимание, что доступ к членам union
небезопасен,и вы должны убедиться, что все инварианты соблюдаются.Например, вы должны явно проверить, что CompactType
s правильно созданы со значениями в пределах диапазона, и предотвратить возможность создания объектов без такой проверки.
Я бы предложил добавить конструкторфункции CompactType
, которые возвращают Result
или Option
, если вы пытаетесь использовать слишком большое число или указатель на тип, который не выровнен должным образом.Когда TryFrom
функция стабилизируется, вы можете использовать это, но в то же время:
enum CompactConvertError {
NumTooBig(String),
PtrNotAligned(String),
}
impl<'ts> Type<'ts> {
fn to_compact(&self) -> Result<CompactType<'ts>, CompactConvertError> {
match self {
Type::TVar(num) => {
if num >> (mem::size_of::<usize>() * 8 - 1) == 1 {
Err(CompactConvertError::NumTooBig(
String::from("The last bit of the usize cannot be used here"))
)
} else {
Ok(CompactType { num: num << 1 | 1usize })
}
},
Type::Constructed(c) => {
if mem::align_of_val(*c) % 2 == 1 {
Err(CompactConvertError::PtrNotAligned(
String::from("The pointer must be to a type with even alignment"))
)
} else {
Ok(CompactType { ptr: c })
}
}
}
}
}
Это должно быть достаточно гибким, чтобы заменить ConstructedType
параметром универсального типа.Единственное ограничение заключается в том, что вы не должны изменять его со ссылкой на собственное значение, иначе вам придется беспокоиться о его правильном отбрасывании - что пока нельзя сделать для типа union
в стабильном Rust.
Что касается выравнивания, если ConstructedType
имеет размер всего 1 байт, вам необходимо добавить выравнивание, чтобы убедиться, что оно выполняется только по четному байту, в противном случае Rust может выбрать более плотную упаковку:
#[align(2)]
struct ConstructedType<'ts> {
// ...
}
Определенно не добавляйте #[align(2)]
, если размер больше 2 байтов.Возможно, кто-то еще может посоветовать, как сделать эту часть более надежной.