Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Default field values #3681

Merged
merged 37 commits into from
Nov 3, 2024
Merged

Conversation

estebank
Copy link
Contributor

@estebank estebank commented Aug 22, 2024

Allow struct definitions to provide default values for individual fields and
thereby allowing those to be omitted from initializers. When deriving Default,
the provided values will then be used. For example:

#[derive(Default)]
struct Pet {
    name: Option<String>, // impl Default for Pet will use Default::default() for name
    age: i128 = 42, // impl Default for Pet will use the literal 42 for age
}

Rendered

Tracking:

@scottmcm scottmcm added the T-lang Relevant to the language team, which will review and decide on the RFC. label Aug 22, 2024
}
```

These can then be used in the following way with the Functional Record Update syntax, with no value:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should call this FRU, since there's no base object.

My preference would be to say this RFC doesn't actually touch FRU at all, just expands struct field expressions (and the derives and such)...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's useful if the RFC draws parallel to the FRU syntax whenever possible, to avoid needing to re-explain things like how struct initializer expressions are type checked (especially bc going into the detail of how it actually works means this RFC is gonna get it wrong). But it's worthwhile calling this something different than FRU.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree in the reference section, C-E, but not in the summary.

text/0000-default-field-values.md Outdated Show resolved Hide resolved
@estebank estebank changed the title default-field-values: initial version RFC: Default field values Aug 23, 2024
@estebank estebank changed the title RFC: Default field values Default field values Aug 23, 2024
@estebank estebank changed the title Default field values [RFC] Default field values Aug 23, 2024
Comment on lines 588 to 593
Because this RFC gives a way to have default field values, you can now simply
invert the pattern expression and initialize a `Config` like so (15):

```rust
let config = Config { width, height, .. };
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this RFC actually makes this possible.

It would need an additional feature like allowing

#[non_exhaustive(but_future_fields_will_have_defaults)]
pub struct Config {
    pub width: u16,
    pub height: u16,
}

because #[non_exhaustive] is a license to add private fields without defaults today.

And there are types today like

#[non_exhaustive]
pub struct Foo;

that we really don't want to make creatable by Foo { .. } in other crates, since the reason they have that attribute today is to prevent such construction.


I think that's fine, though: this RFC is useful without such an addition, and thus it can be left as future work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to see if I get you right, you saying that this RFC should disallow #[non_exhaustive] and default fields on the same struct, and leave any interaction as an open question? I'm down with that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that defaults would still be allowed, but the type would remain unconstructable from outside the defining crate. That way you can still use things like a default on PhantomData on internal, non_exhaustive types.

Given that

#[non_exhaustive]
pub struct Foo;

is commonly used to write a type that cannot be constructed outside the crate (replacing the old pub struct Foo(()); way of writing this), I don't think we can ever say that any non_exhaustive types are constructible outside the defining crate without the defining crate opting-in to that somehow. Telling everyone they have to change back to having a private field is a non-starter, in my opinion.

But I'd also be fine with saying that you just can't mix them yet, and make decisions about it later.

Copy link
Contributor

@clarfonthey clarfonthey Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I think it would be useful to preserve the property that Struct { field: value, .. } as an expression (the proposed syntax) is equivalent to Struct { field: value, ..Default::default() }, and as such, these examples would only work if these structs derived or manually implemented Default.

That should cover the API concerns, although it would make Default become a lang item, which I am personally fine with but I am not everyone, so, that would be a downside to this approach.

If it doesn't interact with Default at all, I agree that it shouldn't allow this, since it does break APIs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think requiring Default is unnecessarily limiting, since it would prevent using the nice new syntax with structs where some fields have defaults and others intentionally do not, e.g. if syn::Index (a tuple struct field name) used the new syntax it could be:

pub struct Index {
    pub index: u32,
    pub span: Span = Span::call_site(),
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can get pretty far with linting here.

A lint can check if the body of your Default::default impl is const fn-compatible, and suggest changing it to have inline defaults if so.

One thing I like about this feature is that it means that the vast majority of (braced-struct) Defaults move to being deriveable -- even Vec's Default could, for example! -- and thus anything that's not would start to really stand out, and starting being a candidate for IDE hints and such.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rereading this I believe that the part of the feature that has a bad interaction is allowing the construction of types that have private default field values. Disallowing that in general, or when the type is also annotated with non_exhaustive would eliminate the issue for every cross-crate case, right? The only potential issue would be intra-crate, and that's already out of scope of non_exhaustive, IIUC.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On reading the RFC, I was honestly a little confused on why interaction with non_exhaustive would be a problem.

My understanding is that non_exhaustive means you can't construct the struct outside of the crate. The ability to have defaults shouldn't have any impact on that. And prohibiting use of defaults on a non_exhaustive struct seems unnecessarily restrictive, since that could still be useful within the crate.

Now, there may be value in a non_exhaustive_but_future_fields_have_defaults functionality, but I think that should be a separate attribute, or add an argument to the existing non_exhaustive attribute, not usurp and change the meaning of the current non_exhaustive attribute.

Comment on lines 815 to 817
In particular, the syntax `Foo { .. }` mirrors the identical and already
existing pattern syntax. This makes the addition of `Foo { .. }` at worst
low-cost and potentially cost-free.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One drawback that comes to mind is that it'll mean that a pattern Foo { .. } can match more things than just the expression Foo { .. }, because the pattern matches any value of the unmentioned fields, but the expression sets them to a particular value.

That means that, with the unstable inline_const_pat, the arm const { Foo { .. } } => matches less than the arm Foo { .. } => (assuming a type like struct Foo { a: i32 = 1 }).

I think I'm probably fine with that, but figured I'd mention it regardless.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is an excellent call-out of a non-obvious interaction I hadn't accounted for.

Copy link
Contributor

@max-niederman max-niederman Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it would be worth adding syntax alternatives like Foo { ... } or Foo { ..some_keyword } to the "Rationale and alternatives" section.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that is already the case?
x if x == Foo { a, b } matches less than Foo { a, b } assuming a and b variables are in scope.
Although f you define a and b as constants then Foo { a, b } will match the exact value, which is.... interesting.

I don't think this is a problem, it is expected that patterns behave differently from expressions.

Copy link
Member

@RalfJung RalfJung Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In particular, the syntax Foo { .. } mirrors the identical and already
existing pattern syntax.

Indeed, and that's a downside -- it mirrors the syntax but has different semantics. The text here makes it sound like that's a good thing; I think that should be reworded (and not just called out under "Downsides").

text/0000-default-field-values.md Outdated Show resolved Hide resolved
text/0000-default-field-values.md Outdated Show resolved Hide resolved
text/0000-default-field-values.md Outdated Show resolved Hide resolved
text/0000-default-field-values.md Outdated Show resolved Hide resolved
text/0000-default-field-values.md Outdated Show resolved Hide resolved
}
```

```rust
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's worth also comparing against derive macros like typed-builder, which at least in my experience are a very common solution to this problem.

text/0000-default-field-values.md Outdated Show resolved Hide resolved
text/0000-default-field-values.md Outdated Show resolved Hide resolved
text/0000-default-field-values.md Outdated Show resolved Hide resolved
facilitates specification of field defaults; or it can directly use the default
values provided in the type definition.

### `structopt`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe replace with clap? since clap v3, basically all structopt stuff was integrated into clap and structopt is de-facto deprecated. https://docs.rs/structopt/0.3.26/structopt/index.html#maintenance

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is a holdover from the original RFC written a number of years ago when structopt was in common usage.

text/0000-default-field-values.md Outdated Show resolved Hide resolved
text/0000-default-field-values.md Outdated Show resolved Hide resolved
text/0000-default-field-values.md Outdated Show resolved Hide resolved
text/0000-default-field-values.md Outdated Show resolved Hide resolved
```

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see a section on differences from FRU (especially the interaction with privacy and #[non_exhaustive]) including why it is the way it is and the reason for diverging from it in this RFC. And possibly a future possibility about how they can be made more consistent in the future.

I'm generally a big fan of this RFC, but undoing past mistakes by adding more features without fixing the features we have leads to an uneven and complex language surface. So let's try to make sure we can do both.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it is reasonable to change this RFC to explicitly follow RFC-0736, and disallow the construction of types with private fields, if that will make this RFC less controversial. Particularly when considering the interaction with #[derive(Default)], the feature is useful on its own without expanding the field privacy rules.

Copy link
Member

@tmandry tmandry Aug 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not saying it's wrong, but I want to understand and consider the rationale for RFC-0736. I dislike it, but think there was probably a good reason for it, even if I can't remember what it was at the moment. That reason may or may not apply here.

edit: I would be happy to help with this btw, it's just too late for me to do right now :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with FRU is all about it not having the desugaring that lots of people expect.

There would be no problem with FRU and privacy if it only allowed you to modify public fields on a type, but the problem is that what it does is secretly duplicate private fields on a type, which is blatantly wrong for literally-but-not-semantically Copy types like the pointer in a Vec. https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/struct.20update.20syntax.20with.20non.20exhaustive.20fields/near/438944351

I think the way forward here is to fix FRU (sketch in https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/struct.20update.20syntax.20with.20non.20exhaustive.20fields/near/438946306) so that it can obviously work fine with non_exhaustive (and private fields), rather than try to work in an exception here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sketch uses a completely new syntax, so we wouldn't be fixing FRU so much as deprecating it and replacing it with something else.

It seems better to design this one to work the way we want from the beginning? Especially since people will have to opt in and might rely on restrictions we implement today (even if we document that those restrictions will be lifted in the future.. people don't usually read the docs on language features so much as try them out in the playground).

estebank added a commit to estebank/rust that referenced this pull request Oct 26, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```
@Ciel-MC
Copy link

Ciel-MC commented Oct 26, 2024

Interesting, this also allows pseudo-optional args for functions. But I am a little worried about mixing data structures with code. When I see a literal struct construction, my mind puts it as “simple and free”, when I see a function, my brain goes “something is happening here”, I feel like this kind of goes against the idea of being explicit about when things happen… Am I overthinking?

@tmccombs
Copy link

my mind puts it as “simple and free”

With the initial expression being constrained to const expressions, most of the work is done at compile time, and at runtime is still simple and almost free

hughsie added a commit to fwupd/fwupd that referenced this pull request Oct 26, 2024
This moves the syntax we chose to align closer with actual Rust code.

See rust-lang/rfcs#3681 for more details.
superm1 pushed a commit to fwupd/fwupd that referenced this pull request Oct 28, 2024
This moves the syntax we chose to align closer with actual Rust code.

See rust-lang/rfcs#3681 for more details.
Co-authored-by: Felix S Klock II <pnkfelix@pnkfx.org>
estebank added a commit to estebank/rust that referenced this pull request Oct 29, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```
@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this RFC. to-announce and removed final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. labels Nov 1, 2024
@rfcbot
Copy link
Collaborator

rfcbot commented Nov 1, 2024

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

@traviscross traviscross merged commit abf3bf8 into rust-lang:master Nov 3, 2024
@traviscross
Copy link
Contributor

The team has accepted this RFC, and we've now merged it.

Thanks to @estebank for writing this up and pushing it forward, and thanks to all the many people who reviewed this and provided helpful feedback.

For further updates, follow the tracking issue:

estebank added a commit to estebank/rust that referenced this pull request Nov 26, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```
estebank added a commit to estebank/rust that referenced this pull request Nov 26, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
estebank added a commit to estebank/rust that referenced this pull request Dec 3, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
estebank added a commit to estebank/rust that referenced this pull request Dec 6, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
estebank added a commit to estebank/rust that referenced this pull request Dec 6, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
estebank added a commit to estebank/rust that referenced this pull request Dec 7, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
estebank added a commit to estebank/rust that referenced this pull request Dec 9, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
estebank added a commit to estebank/rust that referenced this pull request Dec 9, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 10, 2024
…piler-errors

Introduce `default_field_values` feature

Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

We now parse const expressions after a `=` in a field definition, to specify a `struct` field default value.

We now allow `Struct { field, .. }` where there's no base after `..`.

`#[derive(Default)]` now uses the default value if present, continuing to use `Default::default()` if not.

```rust
#[derive(Debug)]
pub struct S;

#[derive(Debug, Default)]
pub struct Foo {
    pub bar: S = S,
    pub baz: i32 = 42 + 3,
}

fn main () {
    let x = Foo { .. };
    let y = Foo::default();
    let z = Foo { baz: 1, .. };

    assert_eq!(45, x.baz);
    assert_eq!(45, y.baz);
    assert_eq!(1, z.baz);
}
```
flip1995 pushed a commit to flip1995/rust-clippy that referenced this pull request Dec 15, 2024
Initial implementation of `#[feature(default_field_values]`, proposed in rust-lang/rfcs#3681.

Support default fields in enum struct variant

Allow default values in an enum struct variant definition:

```rust
pub enum Bar {
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Allow using `..` without a base on an enum struct variant

```rust
Bar::Foo { .. }
```

`#[derive(Default)]` doesn't account for these as it is still gating `#[default]` only being allowed on unit variants.

Support `#[derive(Default)]` on enum struct variants with all defaulted fields

```rust
pub enum Bar {
    #[default]
    Foo {
        bar: S = S,
        baz: i32 = 42 + 3,
    }
}
```

Check for missing fields in typeck instead of mir_build.

Expand test with `const` param case (needs `generic_const_exprs` enabled).

Properly instantiate MIR const

The following works:

```rust
struct S<A> {
    a: Vec<A> = Vec::new(),
}
S::<i32> { .. }
```

Add lint for default fields that will always fail const-eval

We *allow* this to happen for API writers that might want to rely on users'
getting a compile error when using the default field, different to the error
that they would get when the field isn't default. We could change this to
*always* error instead of being a lint, if we wanted.

This will *not* catch errors for partially evaluated consts, like when the
expression relies on a const parameter.

Suggestions when encountering `Foo { .. }` without `#[feature(default_field_values)]`:

 - Suggest adding a base expression if there are missing fields.
 - Suggest enabling the feature if all the missing fields have optional values.
 - Suggest removing `..` if there are no missing fields.
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 16, 2024
…ive, r=<try>

Lint against manual `impl Default` that could have been `derive`d

```
error: `impl Default` that could be derived
  --> $DIR/manual-default-impl-could-be-derived.rs:74:1
   |
LL | / impl Default for G {
LL | |     fn default() -> Self {
LL | |         G {
LL | |             f: F::Unit,
LL | |         }
LL | |     }
LL | | }
   | |_^
   |
help: you don't need to manually `impl Default`, you can derive it
   |
LL ~ #[derive(Default)] struct G {
   |
```

As part of rust-lang#132162/rust-lang/rfcs#3681 we want to lint when default fields values could preclude the need of a manual `impl Default`, but there are already cases where these manual impls could be derived. This PR introduces a new `default_could_be_derived` lint that makes a best effort check of the body of the `Default::default()` implementation to see if all the fields of a single expression in that body are either known to be `Default` already (like an explicit call to `Default::default()`, a `0` literal, or `Option::None` path) or are identified to be equivalent to the field's type's `Default` value (by opportunistically looking at the `Default::default()` body for that field's type).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this RFC. I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. T-lang Relevant to the language team, which will review and decide on the RFC. to-announce
Projects
None yet
Development

Successfully merging this pull request may close these issues.