У меня есть несколько структур с перекрывающимися и уникальными полями. На данный момент я обрабатываю это с помощью перечислений отдельных структур, как показано ниже. Это усложняет доступ к общим полям и упрощает продолжение работы с определенными полями после определения типа:
pub struct MyStructA {
pub a: u64,
pub b: u64,
pub c: u64,
pub d: u64,
}
pub struct MyStructB {
pub a: u64,
pub b: u64,
pub e: String,
pub f: String,
}
pub enum MyStructEnum {
Num(MyStructA),
Str(MyStructB),
}
// This gets annoying with many shared fields
pub fn get_a(m: MyStructEnum) -> u64 {
match m {
MyStructEnum::Num(n) => n.a,
MyStructEnum::Str(s) => s.a,
}
}
// When we know m is MyStructB this is easy
pub fn work_on_b(m: MyStructB) {
println!("{} {}", m.a, m.e)
Поскольку у меня есть много перекрывающихся полей и больше разных типов, поддерживающих get_a()
-подобные функции, это утомительно, и кажется глупым, предполагая, что структура выложена так, как она была бы в C.
Так что я мог бы пойти на что-то вроде
pub struct NewStructA {
pub c: u64,
pub d: u64,
}
pub struct NewStructB {
pub e: String,
pub f: String,
}
pub enum NewStructEnum {
Num(NewStructA),
Str(NewStructB),
}
pub struct NewStruct {
pub a: u64,
pub b: u64,
pub e: NewStructEnum,
}
// Now this is easy
pub fn new_get_a(n: NewStruct) -> u64 {
n.a
}
// But now I can't access a after identifying the type B
pub fn work_on_b(m: NewStructB) {
println!("{} {}", m.a, m.e)
}
Любые способы получить лучшее из обоих миров? В C это было бы легко (и небезопасно).
Вы можете использовать базовую структуру, но внутри перечисления. То есть:
pub struct NewStruct {
pub a: u64,
pub b: u64,
}
pub struct NewStructA {
base: NewStruct,
pub c: u64,
pub d: u64,
}
pub struct NewStructB {
base: NewStruct,
pub e: String,
pub f: String,
}
pub enum NewStructEnum {
Num(NewStructA),
Str(NewStructB),
}
Теперь вы можете определить функцию доступа для base
:
pub fn base(v: &NewStructEnum) -> &NewStruct {
match v {
NewStructEnum::Num(NewStructA { base, .. }) => base,
NewStructEnum::Str(NewStructB { base, .. }) => base,
}
}
А доступ к общему полю так же прост, как base(&s).a
. Вам также может понадобиться создать base_mut()
. Конечно, вы все еще можете получить доступ к полям, когда у вас есть ссылка на конкретный тип.
Это не так хорошо, как с C, но тем не менее довольно хорошо.
@mrtnlrsn Ниша мешает оптимизации. Если вы не навязываете нишу #[repr(C)]
перечислению, оно оптимизирует.
Спасибо. Все остальные: больше информации о нише: rust-lang.github.io/unsafe-code-guidelines/glossary.html#niche
Выглядит хорошо. Есть ли у вас какие-либо идеи, может ли компилятор оптимизировать все это? Поскольку база находится первой в структуре, все руки соответствия, вероятно, делают то же самое. Это не важно для моего варианта использования, но может быть для других.