Using ..Default::default() in a non_exhaustive struct fails

I am trying to use mongodb library which has a lot of non_exhaustive structs and compiler fails to compile when I do

let options = UpdateOptions{
    upsert:Some(true),
    ..Default::default()
}

however

let mut options = UpdateOptions::default();
options.upsert=Some(true)

compiles just fine.
Since if struct T implements default all its fields must also implement default so I am failing to understand the difference between these 2 versions.

The struct update syntax, aka functional update syntax, cannot be used for non-exhaustive structs.

It isn't anything to do with the Default trait specifically; what you were trying to do was to create a temporary UpdateOptions using Default::default(), and then using the struct update syntax to move the values from the temporary into your new struct. You could use ..variable too. In particular, the syntax doesn't call Default::default() on all the fields or anything like that.

See also the reference chapter on #[non_exhaustive]. It also links to a section on the struct update syntax.

Since if struct T implements default all its fields must also implement default

That's also not true incidentally. If a struct T derives Default, all it's fields must also implement Default. But it can implement Default even if its fields do not.

thanks for the answer It clarified things a lot better and it is really unintuitive that they designed it this way in my opinion.
If anyone has similar issues I've written a small macro to get over this issue

macro_rules! mongo_opt{
    ($($k:ident = $val:expr)+)=>{
        {
            let mut opt = Default::default();
            $(opt.$k=Some($val);)+
            opt
        }
    };
    ($t:ty; $($k:ident = $val:expr)+)=>{
        {
            let mut opt:$t = Default::default();
            $(opt.$k=Some($val);)+
            opt
        }
    }
}

1 Like

Yeah, pretty much nobody expects FRU to work the way it does. Unfortunately we looked at changing it and it's a breaking change because of some corner cases.

But there are conversations going on about other options. See this IRLO thread, for example: https://internals.rust-lang.org/t/pre-pre-rfc-syntactic-sugar-for-default-default/13234?u=scottmcm

Incidentally, that thread also has a macro that might interest you, as it's very similar to the one you wrote but also magically allows not specifying the type:

1 Like

that is an awesome trick :smiley: pretty smelly tho :smiley: