1

I'm working on a project where I have a struct for which I want a default implementation.

#[derive(Debug)]
struct State {
    view_type: ViewType,
    booking_state: booking::State,
    calendar_state: calendar::State,
    history_state: history::State,
    configurator_state: configurator::State,
    theme: ds::widget::ComboBoxState<iced::Theme>, // iced::Theme doesn't impl IntoEnumIter so default implementation cannot be used
}

All fields in this struct except the theme (which due to some trait limitations can't) implement Default. Meaning that I can't derive Default and have to implement it manually:

impl std::default::Default for State {
    fn default() -> Self {
        Self {
            view_type: ViewType::default(),
            booking_state: booking::State::default(),
            calendar_state: calendar::State::default(),
            history_state: history::State::default(),
            configurator_state: configurator::State::default(),
            theme: ds::widget::ComboBoxState::new(
                wgt::combo_box::State::new(iced::Theme::ALL.to_vec()),
                Some(iced::Theme::Dark),
            ),
        }
    }
}

Is there a way to do this where I just define the default for the theme field and tell rust to use the default values for the remaining fields? I.e. something like:

impl std::default::Default for State {
    fn default() -> Self {
        Self {
            // view_type: ViewType::default(),
            // booking_state: booking::State::default(),
            // calendar_state: calendar::State::default(),
            // history_state: history::State::default(),
            // configurator_state: configurator::State::default(),
            theme: ds::widget::ComboBoxState::new(
                wgt::combo_box::State::new(iced::Theme::ALL.to_vec()),
                Some(iced::Theme::Dark),
            ),
            ..Default::default() // recurses rather than using default values for remaining fields.
        }
    }
}
3
  • There is probably a crate that provides this functionality, but I can't find a popular one. Commented Jun 26 at 12:05
  • One hack is to put all Default fields in their own struct with Default derived so that you can just write Self { theme: ComboBoxState::new(...), state: Default::default() }, although the cure may be worse than the disease. Commented Jun 26 at 15:04
  • @BallpointBen exactly I'd have to go through another layer to get at any of those fields so that just shifts the pain in the arse to another area and tbh makes it way bigger Commented Jul 1 at 20:49

3 Answers 3

0

Is there a way to do this where I just define the default for the theme field and tell rust to use the default values for the remaining fields? I.e. something like:

Not directly, .. does not support partial / structural completion, and Default even less so.

You can use something like bon, but defaults are opt-in per-member e.g.

use bon::Builder;
#[derive(Builder, Debug)]
struct Foo {
    #[builder(start_fn)]
    a: String,
    #[builder(default)]
    b: u32,
    #[builder(default)]
    c: u8
}
fn main() {
    println!("{:?}", Foo::builder("bar".into()).build());
    println!("{:?}", Foo::builder("bar".into()).c(5).build());
}
Foo { a: "bar", b: 0, c: 0 }
Foo { a: "bar", b: 0, c: 5 }

there might be a similar library which defaults by default when it can, but I don't know it. Maybe check the various alternatives to bon?

edit: looks like typed-builder and derive_builder at least are both opt-in for per-member defaults.

Sign up to request clarification or add additional context in comments.

Comments

0

You can use the educe crate for this kind of thing. For example:

use educe::Educe;

struct Bar {} // doesn't implement default

#[derive(Educe)]
#[educe(Default)]
struct Struct {
    f1: u8,                     // uses Default for u8
    #[educe(Default(expression = Bar {}))]
    f2: Bar,
}

2 Comments

educe also has not received either a commit or an update in a year so would also appear to be abandoned
Perhaps a bit too early to declare, but I get your point. :/
0

You can use the derivative crate for that:

#[derive(Debug, Derivative)]
#[derivative (Default)]
struct State {
    view_type: ViewType,
    booking_state: booking::State,
    calendar_state: calendar::State,
    history_state: history::State,
    configurator_state: configurator::State,
    #[derivative (Default (value = "ds::widget::ComboBoxState::new (wgt::combo_box::State::new (iced::Theme::ALL.to_vec()), Some (iced::Theme::Dark))"))]
    theme: ds::widget::ComboBoxState<iced::Theme>, // iced::Theme doesn't impl IntoEnumIter so default implementation cannot be used
}

4 Comments

Please note that derivative appears to be abandoned. This is why I recommended educe in my answer (and moved our codebases at work from derivative to educe).
Combobox state does have a default implementation (Just one which requires IntoEnumIterator) and this appears to be overriding derivative i.e. the code is refusing to compile due to the lack of the IntoEnumIterator trait despite derivative instructing it to use a default value which does not require this trait.
It's probably an edge case but unfortunately means derivative doesn't solve the problem in this case
You can override the implicit bounds with #[derivative (Default (bound = ""))]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.