Skip to content

New lint to ensure Default::default calls T::new, rather than vice versa. #12662

Open

Description

@solarretrace

What it does

Detects cases where a parameterless new simply calls Default::default, and indicates that default should call new, rather than the opposite.

Advantage

new functions can potentially be const, while default cannot, and calling them in the opposite order can prohibit this.

Drawbacks

The lint is completely irrelevant if the new function is never intended to be const.

Example

pub struct Config;

impl Default for Config {
    fn default() -> Self { Self }
}

impl Config {
    pub fn new() -> Self {
        Self::default()
    }
}

Could be written as:

pub struct Config;

impl Default for Config {
    fn default() -> Self { Self::new() }
}

impl Config {
    pub fn new() -> Self {
        Self
    }
}

Activity

  1. Jarcho commented on Apr 12, 2024

    @Jarcho
    Contributor

    This is going to end up with way too many false-positives and be more work to write and maintain if Default is derived. This would have to be limited to Default impls that could be const if they weren't a trait function. Probably only for exported types as well.

  2. nyurik commented on Jan 24, 2025

    @nyurik
    Contributor

    I would actually suggest the opposite IF default was derived. Case to the point is a bug I just caught in the core lib - rust-lang/rust#135977 -- where fn new would instantiate struct with non-zero values, while default() was auto-derived.

  3. nyurik commented on Jan 25, 2025

    @nyurik
    Contributor

    I created a new lint suggestion that detects non-matching new and default - #14075

  4. nyurik commented on Jan 31, 2025

    @nyurik
    Contributor

    After some more thinking, I think this would actually be a bad pattern, at least for the cases when the Default impl is auto derived. If Default is not autoderived, this same pattern should still be followed for consistency.

    // User code should follow this pattern when possible:
    
    #[derive(Default)]
    pub struct Config;
    
    impl Config {
        pub fn new() -> Self {
            Self::default()
        }
    }
  5. nyurik commented on Jan 31, 2025

    @nyurik
    Contributor

    @Skgland I see you disagreed - could you clarify why? Do you think new() -> Self should have a different implementation than the auto-generated Default trait?

  6. Skgland commented on Jan 31, 2025

    @Skgland
    Contributor

    If Default is not autoderived, this same pattern should still be followed for consistency.

    Was the part I disagree with.

    In case default is derived I would agree that it's reasonable to have new call default, but in the case where default is manually implemented having default call new so the later can be made const is in my opinion preferable (at least until const Traits are available).

  7. nyurik commented on Jan 31, 2025

    @nyurik
    Contributor

    ah, yes, that does make sense, thx. Hopefully rust-lang/rust#67792 will get stabilized at some point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintArea: New lints

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions