-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
add default_mismatches_new
lint
#14234
base: master
Are you sure you want to change the base?
Conversation
new_without_default
lintdefault_mismatches_new
lint
c634158
to
da55789
Compare
default_mismatches_new
lintdefault_mismatches_new
lint
da55789
to
fc5efa3
Compare
default_mismatches_new
lintdefault_mismatches_new
lint
There will be a false positive on this one and the suggestion won't apply: #[derive(Default)]
struct S<T> {
o: Option<T>,
}
impl<T> S<T> {
fn new() -> Self {
S { o: None }
}
} |
@samueltardieu good catch! Auto-derive implements it with a impl<T: Default> Default for S<T> {
fn default() -> S<T> {
S { o: Default::default() }
}
} What logic does the Default derive uses? Does it simply Possible solutions
|
For now, I restricted this lint to non-generic types. Lets do generics separately. |
Constraints are additive, you would have also to consider the constraints on the enclosing |
@samueltardieu thx, so lets skip types with generics for now - even without generics this catches all the same cases as part of CI lint check. We can track it in a separate issue/PR. |
6990299
to
b9ece48
Compare
### What it does If a type has an auto-derived `Default` trait and a `fn new() -> Self`, this lint checks if the `new()` method performs custom logic rather than simply calling the `default()` method. ### Why is this bad? Users expect the `new()` method to be equivalent to `default()`, so if the `Default` trait is auto-derived, the `new()` method should not perform custom logic. Otherwise, there is a risk of different behavior between the two instantiation methods. ### Example ```rust #[derive(Default)] struct MyStruct(i32); impl MyStruct { fn new() -> Self { Self(42) } } ``` Users are unlikely to notice that `MyStruct::new()` and `MyStruct::default()` would produce different results. The `new()` method should use auto-derived `default()` instead to be consistent: ```rust #[derive(Default)] struct MyStruct(i32); impl MyStruct { fn new() -> Self { Self::default() } } ``` Alternatively, if the `new()` method requires a non-default initialization, implement a custom `Default`. This also allows you to mark the `new()` implementation as `const`: ```rust struct MyStruct(i32); impl MyStruct { const fn new() -> Self { Self(42) } } impl Default for MyStruct { fn default() -> Self { Self::new() } } ```
b9ece48
to
b36567a
Compare
@samueltardieu should @llogiq or someone else be assigned as the reviewer? (per your priv comment) |
This should not be necessary as the issue is assigned to him already, but just in case I've requested his review. |
/// } | ||
/// ``` | ||
/// | ||
/// Alternatively, if the `new()` method requires a non-default initialization, implement a custom `Default`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would unify the new
and default
implementation, losing either one or the other. I think the docs should suggest renaming new
to clarify the difference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@llogiq thx for review! Could you clarify how you think the docs should change? The rustc
issue that actually caused me to write this lint was that new()
would set the separator to ' '
, whereas the default()
set it to '\0'
. So unifying them should loose one or the other, and consolidate the code, but keep the original function as new()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P.S. are there any blockers to start the FCP?
Implement #14075. Prevents rust-lang/rust#135977.
What it does
If a type has an auto-derived
Default
trait and afn new() -> Self
, this lint checks if thenew()
method performs custom logic rather than callingSelf::default()
Why is this bad?
Users expect the
new()
method to be equivalent todefault()
, so if theDefault
trait is auto-derived, thenew()
method should not perform custom logic. Otherwise, there is a risk of different behavior between the two instantiation methods.Example
Users are unlikely to notice that
MyStruct::new()
andMyStruct::default()
would produce different results. Thenew()
method should use auto-deriveddefault()
instead to be consistent:Alternatively, if the
new()
method requires non-default initialization, implement a customDefault
. This also allows you to mark thenew()
implementation asconst
:.stderr
file)cargo test
passes locallycargo dev update_lints
cargo dev fmt
changelog: [
default_mismatches_new
]: new lint to catchfn new() -> Self
method not matching theDefault
implementationNotable cases in the wild
default()
andnew()
are actually different - likely was a bug that glob team wants to fix (per comments)new()
creates cache withVec::with_capacity()
, whereasdefault()
usesVec::default()
. Possibly a bug.Self(())
-- should this be ignored?Self { _p: () }
forstruct Identity { _p: () }
-- probably better to use::default()
TODO/TBD
fn new() -> Self
implementations are OK? (rather than delegating toSelf::default()
)Self
is a unit typerustc_hir::hir::ImplItem
of afn new() -> Self
, get the body of that functionreturn Self::default();
and no other logic (in all variants likeDefault::default()
, etc.clippy_lints/src/default_constructed_unit_structs.rs
while trying to understand its logic - I think we may want to have ais_unit_type(ty)
utility function?