Pre-pre-RFC: syntactic sugar for `Default::default()`

No. It means that the Default proc macro in the library (well, technically in the compiler right now, but conceptually is in the library) needs to read those expressions from the token stream.

(If you don't want Default to be a lang item, you might be interested in #77688.)


11 posts were split to a new topic: Making Default a lang item so that function pointers can implement it

It certainly seems to keep coming up in discussion. Is there procedure for reopening a paused RFC unchanged, if the "post" it was phoned to happens to be now? Would it fall on e.g. @ekuber to lead the charge if @\KodrAus is no longer available?

Since the content of RFCs is usually dual-licensed under MIT/Apache-2.0, I guess that anyone can submit the same RFC again.

However, the new RFC should also explain what has changed since the previous RFC was closed/postponed, that justifies merging it now.

So something along the lines of

This PR reopens <link to last one>, which was postponed due to edition crunch. Besides [ongoing](link to @Centril's [interest](link zulip) [in](link here) the feature, and those outlined in the original RFC, motivations are

  • The original closure reason no longer applies
  • const expressions are much more usable than they were in 2016, making the restriction less limiting.
  • there seems to be rough agreement on specific Struct { a, b, .. } syntax.

I still don't get why we can't use Haskell way for this problem - include a def() function in Prelude, that is polymorphic on return type.

That's what the briefly discussed fn default<T: Default>() -> T { Default::default() } is, which is enough for the original proposal. But there's interest in also have support for partial defaults which would require language level support, not just a function or trait.

1 Like

The problem is that since Default::default() is not const (and runs arbitrary code), this doesn't have the same behaviour as possbile partial defaults which are const expressions. As mentioned here:

I think the question is, what are the use-cases? The most concrete one I saw here are "options"-structs, which benefit mostly from partial defaults and support for #[non_exhaustive] structs. In the meantime, I think a free-function default gets most of the benefits of a mid-term solution, while being cheap to implement and avoiding potentially introducing conflicts if the { .. } syntax (as opposed to { ..default() }) will be adopted for the full version.


I also think that Foo { a, b, .. } should expand to the const expressions specified in the struct declaration, while Foo { a, b, ..default() } would use Default::default(). Furthermore, if both a and b are const, then Foo { a, b, .. } would be const too, but the not Foo { a, b, ..default() }.


It seems reasonable to make this distinction (and the possibility of sometimes using .. to imply ..default() when possible could be left as potential future work, but doesn't need to ever be done). The only concern I would have then is teachability: there are two slightly different ways of doing the same thing with different trade-offs.

What would be the behavior on the definition side?

struct A {
   b : Option<u8>,
   c : u8 = 43; // has to be a const expression

Would mean that this would impl Default for A and would also have a way of instantiating it with A { b: None, .. }? I would be ok with it (and we could taper of the sharp edge of calling A { .. } with appropriate diagnostics).

BTW, we cannot in good conscience cause the above definition to not work with the partial constructor syntax because derive macros are not capable of rewriting their token stream (we potentially could with Default because it is an intrinsic, but we very much shouldn't).

1 Like

Thinking about diagnostics, there are a few cases to consider:

  • using A { .. } when all fields are not defaulted to const expressions
    • has Default suggest ..default()
    • always, suggest fields that require values, if those impl Default, then the suggested placeholder expression would be Default::default()
  • A { ..default() } when it doesn't impl Default
    • has some const fields, suggest A { non_const: <expr>, .. }
  • A { ..binding } when binding doesn't exist, same as first bullet point

Should the following imply #[derive(Default)]? I think it might, but with the suggestion mentioned above it wouldn't be needed.

struct A {
   b : Option<u8> = None,
   c : u8 = 43;

I think not, and I'd justify that by how we handle Copy -- you can copy all fields with A { ..*ref_a } even though A does not implement Copy. I think we can say the same for Default, that having defaults on the fields is distinct from Default on the whole.


I agree with cuviper that probably not. I think I'd expect to see it in the derive list.

My first instinct is that I'd be in favour of a lint suggesting it, though, since I think that if A { .. } works then there's no reason to not also have A::default() work.

The thing that comes to mind is that syntactically we could allow arbitrary expressions in the field default value, which would be checked later for being const, but way we could allow arbitrary expressions to be used for #[derive(Default)]... But accomplishing that would turn Default into something that wouldn't be able to be implemented by 3rd party libs in its entirety. I would leave this as "possible future work" that I do not expect to be done unless there's a lot of feedback about people wanting this once the rest of the feature gets heavy use.

:+1:; always good to start with tight restrictions since loosening them is easier than adding them later.


I wrote a macros for it Not sure it works for all possible cases but definitely works for some :slight_smile:


An interesting bit of relatively-new prior art for having something like this, C#9 added "target-typed new" which allows constructing an object (including an object initializer, which does the what-people-initially-thing-FRU-does) without needing to type out its type.

1 Like

First of all, thank you @ekuber for putting the work into this RFC-to-be. I definitely think that the ergonomics around Default::default() can and should be improved.

I'm not so sure about the addition of a magic syntax though, instead of addressing some fundamental problems with the way that the ..Default::default() "operator" works.

(I also haven't read all comments on this, some of which seemed to be going in a very different direction from the actual RFC?)

In short: I think adding a new syntax sugar for nested Default invocations would feel inconsistent, if for example nested match defaults ( _ => ... ) are not similarly handled. Especially because ..Default::default() is quite recognisable, whereas ..* is really not. I also don't think that just shortening the function name is really going to be a big quality of life improvement. There might be some more possible one-liners, but in the grand scheme of things it doesn't really change anything.

Instead, I think ergonomic improvements around Default should attack the root of the problem, namely that partial default implementations need to be possible.

At least from interacting with other projects, and the projects I work on, the issue is usually having to repeat a lot of fields that should be default-able.

For example:

struct Config {
  path: PathBuf
  strict_mode: bool,
  version: u8

The problem with implementing a Default for this struct is that path will end up having to be an invalid value, and we have to hope that it is instantiated correctly by overriding the field with a better value (i.e. Config { path, ..Default::default() }). If we allow for partial Default implementations, there could be default values for strict_mode and version, but the type system will enforce giving path a concrete value before it is a valid struct.

Without special language support this could be implemented with a derive macro that generates a new struct where all fields are Option<T>, and a transformation from this temporary struct to the real struct, where a builder takes the set of inputs that is required to make it a complete struct.

With some language support this could potentially be much easier to use, and an elegant solution to avoid having stale fields provided by wrong (or incomplete) Default values on a struct.

This suggestion is a bit off what the original RFC-to-be proposes, but I think it's a much better solution to the underlying problem of what makes using Default so cumbersome.

Now, this wouldn't really make 5-nested structs where each has a default any less cumbersome, but I think it might encourage people to write their structs and builders in a way that makes it easier to not end up in these situations.


Thanks @spacekookie!

I know is a long thread so it is a good time to summarize what the RFC I'll be writing will propose, particularly because it is removed from what I originally wrote:

  • Language support for default values on structs through const expressions:

    struct Car {
        wheel_count: usize = 4;
  • Language support for partially defaulted structs:

    struct Person {
        name: String,
        age: i128 = 0;
  • Teach #[derive(Default)] to use the default values

    struct Pet {
        name: Option<String>, // impl Default for Pet will use Default::default() for name
        age: i128 = 0; // impl Default for Pet will use the literal 0 for age
  • Expand the "spread" .. operator to make the RHS expression optional, if it is not present it will use the default const expressions set in the struct definition:

    let person = Person { name: "Jane Austen".to_string(), .. };
    let person = Person { name: "Margaret Atwood".to_string(), ..Default::default() }; // Compilation error, Person doesn't impl Default
    let pet = Pet { name: None, .. };
    let pet = Pet { .. }; // Compilation error, name doesn't have a `const` default value
    let pet = Pet { ..Default::default() }; // Ok
  • Considerations around letting non-const expressions in the struct default are to be punted initially

  • Considerations around making partial defaults a user definable feature are to be left for a future RFC (explicitly not adding a new PartialDefault trait, at least for now)

  • Further conversation around "should a struct that has all default values set automagically impl Default?" and "should a struct that impls Default automagically work with ..?" is needed

  • fn default<T: Default>(t: T) -> T { T::default() } is tangential and independent of this RFC

  • I will be basing the RFC on prior work that has been closed in the past due to lack of bandwidth.

Please everyone, let me know if there's any item that I have forgotten in this quick summary.


In the case that only const-expressions are finally allowed it could be useful to have a ConstDefault trait to have way to initialize some types. Of course, that could be done later.

Also related, I think I have not seen in this thread any example with generic types. It could help to show in the proposal a PhantomData or similar.