-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Summary
In certain cases involving type parameters with defaults, it is possible for clippy to suggest re-writing an impl
block using a derive
, when in fact a derive
would be more generic and may even cause a compiler error.
Lint Name
derivable_impls
Reproducer
I tried this code:
#![allow(dead_code)]
#[derive(Default)]
struct DefaultType;
struct GenericType<T = DefaultType> {
t: T,
}
impl Default for GenericType {
fn default() -> Self {
Self { t: Default::default() }
}
}
This triggers the lint saying the impl
can instead be derived:
$ cargo clippy
warning: this `impl` can be derived
--> src/lib.rs:10:1
|
10 | / impl Default for GenericType {
11 | | fn default() -> Self {
12 | | Self { t: Default::default() }
13 | | }
14 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
= note: `#[warn(clippy::derivable_impls)]` on by default
= help: remove the manual implementation...
help: ...and instead derive it
|
6 | #[derive(Default)]
|
This is a false positive as a derive
here would be semantically different. The impl
block as written implements Default
specifically for GenericType
, which is equivalent to GenericType<DefaultType>
. However a derive
would implement Default
for any GenericType<T: Default>
.
A similar case is the following, with an additional layer of indirection:
#![allow(dead_code)]
#[derive(Default)]
struct DefaultType;
struct InnerGenericType<T> {
t: T,
}
impl Default for InnerGenericType<DefaultType> {
fn default() -> Self {
Self { t: Default::default() }
}
}
struct GenericType<T = DefaultType> {
inner: InnerGenericType<T>,
}
impl Default for GenericType {
fn default() -> Self {
Self { inner: Default::default() }
}
}
This triggers the lint:
$ cargo clippy
warning: this `impl` can be derived
--> src/lib.rs:20:1
|
20 | / impl Default for GenericType {
21 | | fn default() -> Self {
22 | | Self { inner: Default::default() }
23 | | }
24 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
= note: `#[warn(clippy::derivable_impls)]` on by default
= help: remove the manual implementation...
help: ...and instead derive it
|
16 | #[derive(Default)]
|
But following the advice would create a compiler error:
...
#[derive(Default)]
struct GenericType<T = DefaultType> {
inner: InnerGenericType<T>,
}
$ cargo clippy
error[E0277]: the trait bound `InnerGenericType<T>: std::default::Default` is not satisfied
--> src/lib.rs:18:5
|
16 | #[derive(Default)]
| ------- in this derive macro expansion
17 | struct GenericType<T = DefaultType> {
18 | inner: InnerGenericType<T>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `InnerGenericType<T>`
|
= note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `InnerGenericType<T>` with `#[derive(Default)]`
|
6 | #[derive(Default)]
|
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
17 | struct GenericType<T = DefaultType> where InnerGenericType<T>: std::default::Default {
| ++++++++++++++++++++++++++++++++++++++++++++++++
For more information about this error, try `rustc --explain E0277`.
The difference here is that the impl
block implements Default
for GenericType<DefaultType>
, whereas the derive
implements Default
for GenericType<T: Default>
. The derive
therefore has the trait bound that InnerGenericType<T>
implements Default
for all T: Default
, which isn't satisfied: only InnerGenericType<DefaultType>
implements Default
.
(And breathe.)
This is similar to #7655 which was fixed – this one still occurs likely because of the special case with the type parameter having a default (<T = DefaultType>
). The lint isn't triggered if the type parameter is explicitly specified, i.e.:
impl Default for GenericType<DefaultType>
instead of
impl Default for GenericType
Version
rustc 1.67.1 (d5a82bbd2 2023-02-07)
binary: rustc
commit-hash: d5a82bbd26e1ad8b7401f6a718a9c57c96905483
commit-date: 2023-02-07
host: aarch64-apple-darwin
release: 1.67.1
LLVM version: 15.0.6
Additional Labels
No response