Установите для всех оставшихся членов структуры их значения по умолчанию - PullRequest
2 голосов
/ 27 апреля 2020

Предположим, что существует структура B, подобная этой:

struct A {
    /* ... */
}

struct B {
    a: A,
    b: u32,
    c: i8,
    /* ... */
    z: usize
}

A не имеет реализации Default и не имеет значения по умолчанию, которое имеет смысл. Предположим, что b..z все необходимо инициализировать по умолчанию (в данном случае 0), а A - какому-либо значению времени выполнения. Это может выглядеть так:

let b = B {
    a: some_a(), b: 0, c: 0, /* ... */ z: 0,
};

Очевидно, это не идеально. Хуже того, если бы к структуре было добавлено aa, в инициализатор должен был быть добавлен еще один элемент. Использование ..Default::default() не работает, потому что B не имеет реализации Default, потому что любые значения A по умолчанию будут слишком дорогими для вычисления, чтобы потом просто их выбросить. Кроме того, в любом случае мне понадобится очень длинный инициализатор структуры в реализации Default.

Есть ли способ установить оставшиеся элементы структуры в их значения по умолчанию (чтобы b было установлено по умолчанию) из u32) без записи всех имен членов?

РЕДАКТИРОВАТЬ: предпочтительно, чтобы при структурировании инициализаторов при добавлении новых членов было минимальное изменение. Это означает, что это не идеально:

let b = B {
    a: some_a(),
    b: Default::default(),
    /* ... */
    z: Default::default(),
};

1 Ответ

4 голосов
/ 27 апреля 2020

Если у вас большие структуры, вы можете упростить свою жизнь, выведя конструктор или функцию конструктора. Есть несколько ящиков, которые делают это, и популярным является derive_builder.

Используя этот ящик, вы можете сделать что-то вроде:

use derive_builder::Builder;

// this derive will generate a struct called BBuilder
#[derive(Builder)]
struct B {
    a: A,
    #[builder(default = "0")]
    b: u32,
    #[builder(default = "0")]
    c: i8,
    /* ... */
    #[default(default = "0")]
    z: usize
}


fn main() {
    let a = A { ... };

    // all default values for fields b..z
    let b = BBuilder::default().a(a).build().unwrap();

    // or specify just some of the fields
    let b = BBuilder::default()
        .a(a)
        .c(42)
        .z(255)
        .build()
        .unwrap();
}

Добавление новые поля в B не будут влиять на код, который использует BBuilder, если поля имеют значения по умолчанию. Недостатком является то, что вы получите панику времени выполнения c, если пропустите обязательное поле, а не ошибку компиляции.


Другой ящик - derive-new, который является немного проще. Вы могли бы использовать это так:

use derive_new::new;

#[derive(new)]
struct B {
    a: A,
    #[new(default)] 
    b: u32,
    #[new(default)] 
    c: i8,
    /* ... */
    #[new(default)]
    z: usize
}

fn main() {
    let a = A { ... };

    // all default values for fields b..z
    let b = B::new(a);

    // To specify some of the default fields, you need to mutate
    let mut b = B::new(a);
    b.c = 42;
    b.z = 255;
}

Это не создает никаких дополнительных структур, просто добавляет метод new, который принимает все аргументы не по умолчанию. Это ошибка компиляции, если вы пропустите поля не по умолчанию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...