Extend field Init Shorthand

In struct initialisation there are two useful shorthands:

MyStruct {
      x: 3,
      ..Default::default()
}

and if there is a String named y in scope that can be used to initialise the struct

MyStruct {
      y,
      x: 2,
}

What is preventing Rust from simplifying even more the second case and allow:

MyStruct {
      x: 2,
      ..
}

Indicating then that any missing fields should be initialised with the variables in scope that have the same name.

1 Like

Rust prefers being explicit. Setting that aside, consider this situation:

  • MyStruct is nonexhaustive
  • a new field is added
  • that field shares a name with a local variable

Now some arbitrary local variable is implicitly exposed, sharing who knows what with other users of the struct instance.

9 Likes

When you add a new field to a struct, usually you would want compilation errors in all places where there isn’t already a clear (and intentional) way of initializing the field. When using ..Default::default(), you already give up some of this, these initializations will keep compiling, but at least, it should still be sensible behavior in most cases.[1] However, if your proposed .. happens to make use of a variable that just happens to have the same name of the newly added field, it’s quite often not intended behavior - so you would have introduced a bug into your program during the refactor, and the compiler never pointed you to the use-site of your struct that would’ve needed to be updated.


So to answer the question, I’d say ultimately it’s a design decision. There’s nothing technical hindering Rust from implementing such a feature, but to me it feels not in line with the usual explicitness, and it can have negative consequences as outlined above. For example, if I recall correctly (edit: here’s the docs), Haskell (or at least GHC) does offer such a version of its “functional update” syntax, where the fields are implicitly taken from corresponding variables in scope. Interestingly, to be analogous to the Haskell feature, if the MyStruct { x: 2, .. } expression reads all other fields by their name from variables in scope, then the MyStruct { x: 2, .. } pattern would also need to introduce variables for all unmentioned fields. (Which it doesn’t, and we cannot simply change that, so that may be another argument against having such a feature with the exact syntax you proposed.)


  1. In the case of #[non_exhaustive] structs, the fact that this keeps compiling is commonly crucial for the struct being usable at all, and very intentional, and in such a case always sensible ↩︎

2 Likes

I see, the conflict between struct initialisation and pattern matching clause did cross my mind, but thought it would be easy to treat those separately. On a second thought, its true it would not be a very good design choice to have the same syntax with different behaviours.

To me, the first argument was enough to realise why this would not be ideal! Thanks!

1 Like

I feel like there is also a readability issue here: looking at

MyStruct {
      x: 2,
      ..
}

all by itself, it seems to me people would expect this to mean something like, perhaps exactly the same as, your first case, with ..Default::default().

8 Likes

I'm still hoping we get this RFC from ekuber as the meaning for ..:

4 Likes

I've been procrastinating on it, but I do want to push on this. I did sneak parser support for part of this feature, and was thinking about adding more support for the feature purely as a way to produce more accurate diagnostics (tell you to use impl Default when it could be done only). I lean this way because it is a "cheap" way of exercising the feature without having us commit to a specific behavior before actually evaluating real behavior against a wider set of examples (which might uncover problems not yet mentioned in that thread).

7 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.