Возможно, я не понимаю всего, что вы говорите, но я думаю, что вам нужен 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 байт. Возможно, кто-то еще может посоветовать, как сделать эту часть более надежной.
person
Peter Hall
schedule
08.09.2018