Ржавые структуры с перекрывающимися полями

У меня есть несколько структур с перекрывающимися и уникальными полями. На данный момент я обрабатываю это с помощью перечислений отдельных структур, как показано ниже. Это усложняет доступ к общим полям и упрощает продолжение работы с определенными полями после определения типа:

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 это было бы легко (и небезопасно).

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
0
72
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете использовать базовую структуру, но внутри перечисления. То есть:

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 22.11.2022 10:21

@mrtnlrsn Ниша мешает оптимизации. Если вы не навязываете нишу #[repr(C)] перечислению, оно оптимизирует.

Chayim Friedman 22.11.2022 10:32

Спасибо. Все остальные: больше информации о нише: rust-lang.github.io/unsafe-code-guidelines/glossary.html#nic‌​he

mrtnlrsn 22.11.2022 10:48

Другие вопросы по теме