Theme

What it does

Checks for usage of items through absolute paths, like std::env::current_dir.

Why restrict this?

Many codebases have their own style when it comes to importing, but one that is seldom used is using absolute paths everywhere. This is generally considered unidiomatic, and you should add a use statement.

The default maximum segments (2) is pretty strict, you may want to increase this in clippy.toml.

Note: One exception to this is code from macro expansion - this does not lint such cases, as using absolute paths is the proper way of referencing items in one.

Known issues

There are currently a few cases which are not caught by this lint:

  • Macro calls. e.g. path::to::macro!()
  • Derive macros. e.g. #[derive(path::to::macro)]
  • Attribute macros. e.g. #[path::to::macro]

Example

let x = std::f64::consts::PI;

Use any of the below instead, or anything else:

use std::f64;
use std::f64::consts;
use std::f64::consts::PI;
let x = f64::consts::PI;
let x = consts::PI;
let x = PI;
use std::f64::consts as f64_consts;
let x = f64_consts::PI;

Configuration

  • absolute-paths-allowed-crates: Which crates to allow absolute paths from

    (default: [])

  • absolute-paths-max-segments: The maximum number of segments a path can have before being linted, anything above this will be linted.

    (default: 2)

Applicability: Unspecified(?)
Added in: 1.73.0

What it does

Finds items imported through alloc when available through core.

Why restrict this?

Crates which have no_std compatibility and may optionally require alloc may wish to ensure types are imported from core to ensure disabling alloc does not cause the crate to fail to compile. This lint is also useful for crates migrating to become no_std compatible.

Known problems

The lint is only partially aware of the required MSRV for items that were originally in std but moved to core.

Example

use alloc::slice::from_ref;

Use instead:

use core::slice::from_ref;
Applicability: MachineApplicable(?)
Added in: 1.64.0

What it does

Checks for usage of the #[allow] attribute and suggests replacing it with the #[expect] (See RFC 2383)

This lint only warns outer attributes (#[allow]), as inner attributes (#![allow]) are usually used to enable or disable lints on a global scale.

Why is this bad?

#[expect] attributes suppress the lint emission, but emit a warning, if the expectation is unfulfilled. This can be useful to be notified when the lint is no longer triggered.

Example

#[allow(unused_mut)]
fn foo() -> usize {
    let mut a = Vec::new();
    a.len()
}

Use instead:

#[expect(unused_mut)]
fn foo() -> usize {
    let mut a = Vec::new();
    a.len()
}

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.70.0

What it does

Checks for attributes that allow lints without a reason.

Why restrict this?

Justifying each allow helps readers understand the reasoning, and may allow removing allow attributes if their purpose is obsolete.

Example

#![allow(clippy::some_lint)]

Use instead:

#![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: Unspecified(?)
Added in: 1.61.0

What it does

Confirms that items are sorted in source files as per configuration.

Why restrict this?

Keeping a consistent ordering throughout the codebase helps with working as a team, and possibly improves maintainability of the codebase. The idea is that by defining a consistent and enforceable rule for how source files are structured, less time will be wasted during reviews on a topic that is (under most circumstances) not relevant to the logic implemented in the code. Sometimes this will be referred to as “bikeshedding”.

Default Ordering and Configuration

As there is no generally applicable rule, and each project may have different requirements, the lint can be configured with high granularity. The configuration is split into two stages:

  1. Which item kinds that should have an internal order enforced.
  2. Individual ordering rules per item kind.

The item kinds that can be linted are:

  • Module (with customized groupings, alphabetical within - configurable)
  • Trait (with customized order of associated items, alphabetical within)
  • Enum, Impl, Struct (purely alphabetical)

Module Item Order

Due to the large variation of items within modules, the ordering can be configured on a very granular level. Item kinds can be grouped together arbitrarily, items within groups will be ordered alphabetically. The following table shows the default groupings:

GroupItem Kinds
modules“mod”, “foreign_mod”
use“use”
macros“macro”
global_asm“global_asm”
UPPER_SNAKE_CASE“static”, “const”
PascalCase“ty_alias”, “opaque_ty”, “enum”, “struct”, “union”, “trait”, “trait_alias”, “impl”
lower_snake_case“fn”

The groups’ names are arbitrary and can be changed to suit the conventions that should be enforced for a specific project.

All item kinds must be accounted for to create an enforceable linting rule set. Following are some example configurations that may be useful.

Example: module inclusions and use statements to be at the top

module-item-order-groupings = [
    [ "modules", [ "extern_crate", "mod", "foreign_mod" ], ],
    [ "use", [ "use", ], ],
    [ "everything_else", [ "macro", "global_asm", "static", "const", "ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl", "fn", ], ],
]

Example: only consts and statics should be alphabetically ordered

It is also possible to configure a selection of module item groups that should be ordered alphabetically. This may be useful if for example statics and consts should be ordered, but the rest should be left open.

module-items-ordered-within-groupings = ["UPPER_SNAKE_CASE"]

Known Problems

Performance Impact

Keep in mind, that ordering source code alphabetically can lead to reduced performance in cases where the most commonly used enum variant isn’t the first entry anymore, and similar optimizations that can reduce branch misses, cache locality and such. Either don’t use this lint if that’s relevant, or disable the lint in modules or items specifically where it matters. Other solutions can be to use profile guided optimization (PGO), post-link optimization (e.g. using BOLT for LLVM), or other advanced optimization methods. A good starting point to dig into optimization is cargo-pgo.

Lints on a Contains basis

The lint can be disabled only on a “contains” basis, but not per element within a “container”, e.g. the lint works per-module, per-struct, per-enum, etc. but not for “don’t order this particular enum variant”.

Module documentation

Module level rustdoc comments are not part of the resulting syntax tree and as such cannot be linted from within check_mod. Instead, the rustdoc::missing_documentation lint may be used.

Module Tests

This lint does not implement detection of module tests (or other feature dependent elements for that matter). To lint the location of mod tests, the lint items_after_test_module can be used instead.

Example

trait TraitUnordered {
    const A: bool;
    const C: bool;
    const B: bool;

    type SomeType;

    fn a();
    fn c();
    fn b();
}

Use instead:

trait TraitOrdered {
    const A: bool;
    const B: bool;
    const C: bool;

    type SomeType;

    fn a();
    fn b();
    fn c();
}

Configuration

  • module-item-order-groupings: The named groupings of different source item kinds within modules.

    (default: [["modules", ["extern_crate", "mod", "foreign_mod"]], ["use", ["use"]], ["macros", ["macro"]], ["global_asm", ["global_asm"]], ["UPPER_SNAKE_CASE", ["static", "const"]], ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], ["lower_snake_case", ["fn"]]])

  • module-items-ordered-within-groupings: Whether the items within module groups should be ordered alphabetically or not.

This option can be configured to “all”, “none”, or a list of specific grouping names that should be checked (e.g. only “enums”).

(default: "none")

  • source-item-ordering: Which kind of elements should be ordered internally, possible values being enum, impl, module, struct, trait.

    (default: ["enum", "impl", "module", "struct", "trait"])

  • trait-assoc-item-kinds-order: The order of associated items in traits.

    (default: ["const", "type", "fn"])

Applicability: Unspecified(?)
Added in: 1.84.0

What it does

Checks any kind of arithmetic operation of any type.

Operators like +, -, * or << are usually capable of overflowing according to the Rust Reference, or can panic (/, %).

Known safe built-in types like Wrapping or Saturating, floats, operations in constant environments, allowed types and non-constant operations that won’t overflow are ignored.

Why restrict this?

For integers, overflow will trigger a panic in debug builds or wrap the result in release mode; division by zero will cause a panic in either mode. As a result, it is desirable to explicitly call checked, wrapping or saturating arithmetic methods.

Example

// `n` can be any number, including `i32::MAX`.
fn foo(n: i32) -> i32 {
    n + 1
}

Third-party types can also overflow or present unwanted side-effects.

Example

use rust_decimal::Decimal;
let _n = Decimal::MAX + Decimal::MAX;

Past names

  • integer_arithmetic

Configuration

  • arithmetic-side-effects-allowed: Suppress checking of the passed type names in all types of operations.

If a specific operation is desired, consider using arithmetic_side_effects_allowed_binary or arithmetic_side_effects_allowed_unary instead.

Example

arithmetic-side-effects-allowed = ["SomeType", "AnotherType"]

Noteworthy

A type, say SomeType, listed in this configuration has the same behavior of ["SomeType" , "*"], ["*", "SomeType"] in arithmetic_side_effects_allowed_binary.

(default: [])

  • arithmetic-side-effects-allowed-binary: Suppress checking of the passed type pair names in binary operations like addition or multiplication.

Supports the “*” wildcard to indicate that a certain type won’t trigger the lint regardless of the involved counterpart. For example, ["SomeType", "*"] or ["*", "AnotherType"].

Pairs are asymmetric, which means that ["SomeType", "AnotherType"] is not the same as ["AnotherType", "SomeType"].

Example

arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]

(default: [])

  • arithmetic-side-effects-allowed-unary: Suppress checking of the passed type names in unary operations like “negation” (-).

Example

arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]

(default: [])

Applicability: Unspecified(?)
Added in: 1.64.0

What it does

Checks for usage of as conversions.

Note that this lint is specialized in linting every single use of as regardless of whether good alternatives exist or not. If you want more precise lints for as, please consider using these separate lints:

  • clippy::cast_lossless
  • clippy::cast_possible_truncation
  • clippy::cast_possible_wrap
  • clippy::cast_precision_loss
  • clippy::cast_sign_loss
  • clippy::char_lit_as_u8
  • clippy::fn_to_numeric_cast
  • clippy::fn_to_numeric_cast_with_truncation
  • clippy::ptr_as_ptr
  • clippy::unnecessary_cast
  • invalid_reference_casting

There is a good explanation the reason why this lint should work in this way and how it is useful in this issue.

Why restrict this?

as conversions will perform many kinds of conversions, including silently lossy conversions and dangerous coercions. There are cases when it makes sense to use as, so the lint is Allow by default.

Example

let a: u32;
...
f(a as u16);

Use instead:

f(a.try_into()?);

// or

f(a.try_into().expect("Unexpected u16 overflow in f"));
Applicability: Unspecified(?)
Added in: 1.41.0

What it does

Checks for the usage of as *const _ or as *mut _ conversion using inferred type.

Why restrict this?

The conversion might include a dangerous cast that might go undetected due to the type being inferred.

Example

fn as_usize<T>(t: &T) -> usize {
    // BUG: `t` is already a reference, so we will here
    // return a dangling pointer to a temporary value instead
    &t as *const _ as usize
}

Use instead:

fn as_usize<T>(t: &T) -> usize {
    t as *const T as usize
}
Applicability: MachineApplicable(?)
Added in: 1.85.0

What it does

Checks for the result of a &self-taking as_ptr being cast to a mutable pointer.

Why is this bad?

Since as_ptr takes a &self, the pointer won’t have write permissions unless interior mutability is used, making it unlikely that having it as a mutable pointer is correct.

Example

let mut vec = Vec::<u8>::with_capacity(1);
let ptr = vec.as_ptr() as *mut u8;
unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR

Use instead:

let mut vec = Vec::<u8>::with_capacity(1);
let ptr = vec.as_mut_ptr();
unsafe { ptr.write(4) };
Applicability: MaybeIncorrect(?)
Added in: 1.66.0

What it does

Checks for the usage of as _ conversion using inferred type.

Why restrict this?

The conversion might include lossy conversion or a dangerous cast that might go undetected due to the type being inferred.

The lint is allowed by default as using _ is less wordy than always specifying the type.

Example

fn foo(n: usize) {}
let n: u16 = 256;
foo(n as _);

Use instead:

fn foo(n: usize) {}
let n: u16 = 256;
foo(n as usize);
Applicability: MachineApplicable(?)
Added in: 1.63.0

What it does

Checks for assert!(r.is_ok()) or assert!(r.is_err()) calls.

Why restrict this?

This form of assertion does not show any of the information present in the Result other than which variant it isn’t.

Known problems

The suggested replacement decreases the readability of code and log output.

Example

assert!(r.is_ok());
assert!(r.is_err());

Use instead:

r.unwrap();
r.unwrap_err();
Applicability: MachineApplicable(?)
Added in: 1.64.0

What it does

Checks for code like foo = bar.clone();

Why is this bad?

Custom Clone::clone_from() or ToOwned::clone_into implementations allow the objects to share resources and therefore avoid allocations.

Example

struct Thing;

impl Clone for Thing {
    fn clone(&self) -> Self { todo!() }
    fn clone_from(&mut self, other: &Self) { todo!() }
}

pub fn assign_to_ref(a: &mut Thing, b: Thing) {
    *a = b.clone();
}

Use instead:

struct Thing;

impl Clone for Thing {
    fn clone(&self) -> Self { todo!() }
    fn clone_from(&mut self, other: &Self) { todo!() }
}

pub fn assign_to_ref(a: &mut Thing, b: Thing) {
    a.clone_from(&b);
}

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: Unspecified(?)
Added in: 1.78.0

What it does

Checks for the usage of the to_be_bytes method and/or the function from_be_bytes.

Why restrict this?

To ensure use of little-endian or the target’s endianness rather than big-endian.

Example

let _x = 2i32.to_be_bytes();
let _y = 2i64.to_be_bytes();
Applicability: Unspecified(?)
Added in: 1.72.0

What it does

Instead of using an if statement to convert a bool to an int, this lint suggests using a from() function or an as coercion.

Why is this bad?

Coercion or from() is another way to convert bool to a number. Both methods are guaranteed to return 1 for true, and 0 for false.

See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E

Example

if condition {
    1_i64
} else {
    0
};

Use instead:

i64::from(condition);

or

condition as i64;
Applicability: MaybeIncorrect(?)
Added in: 1.65.0

What it does

Checks for the usage of &expr as *const T or &mut expr as *mut T, and suggest using &raw const or &raw mut instead.

Why is this bad?

This would improve readability and avoid creating a reference that points to an uninitialized value or unaligned place. Read the &raw explanation in the Reference for more information.

Example

let val = 1;
let p = &val as *const i32;

let mut val_mut = 1;
let p_mut = &mut val_mut as *mut i32;

Use instead:

let val = 1;
let p = &raw const val;

let mut val_mut = 1;
let p_mut = &raw mut val_mut;

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.60.0

What it does

Checks if the if and else block contain shared code that can be moved out of the blocks.

Why is this bad?

Duplicate code is less maintainable.

Example

let foo = if … {
    println!("Hello World");
    13
} else {
    println!("Hello World");
    42
};

Use instead:

println!("Hello World");
let foo = if … {
    13
} else {
    42
};
Applicability: Unspecified(?)
Added in: 1.53.0

What it does

Checks to see if all common metadata is defined in Cargo.toml. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata

Why is this bad?

It will be more difficult for users to discover the purpose of the crate, and key information related to it.

Example

[package]
name = "clippy"
version = "0.0.212"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
license = "MIT OR Apache-2.0"
keywords = ["clippy", "lint", "plugin"]
categories = ["development-tools", "development-tools::cargo-plugins"]

Should include a description field like:

[package]
name = "clippy"
version = "0.0.212"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
license = "MIT OR Apache-2.0"
keywords = ["clippy", "lint", "plugin"]
categories = ["development-tools", "development-tools::cargo-plugins"]

Configuration

  • cargo-ignore-publish: For internal testing only, ignores the current publish settings in the Cargo manifest.

    (default: false)

Applicability: Unspecified(?)
Added in: 1.32.0

What it does

Checks for calls to ends_with with possible file extensions and suggests to use a case-insensitive approach instead.

Why is this bad?

ends_with is case-sensitive and may not detect files with a valid extension.

Example

fn is_rust_file(filename: &str) -> bool {
    filename.ends_with(".rs")
}

Use instead:

fn is_rust_file(filename: &str) -> bool {
    let filename = std::path::Path::new(filename);
    filename.extension()
        .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
}
Applicability: MaybeIncorrect(?)
Added in: 1.51.0

What it does

Checks for casts between numeric types that can be replaced by safe conversion functions.

Why is this bad?

Rust’s as keyword will perform many kinds of conversions, including silently lossy conversions. Conversion functions such as i32::from will only perform lossless conversions. Using the conversion functions prevents conversions from becoming silently lossy if the input types ever change, and makes it clear for people reading the code that the conversion is lossless.

Example

fn as_u64(x: u8) -> u64 {
    x as u64
}

Using ::from would look like this:

fn as_u64(x: u8) -> u64 {
    u64::from(x)
}
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for casts between numeric types that may truncate large values. This is expected behavior, so the cast is Allow by default. It suggests user either explicitly ignore the lint, or use try_from() and handle the truncation, default, or panic explicitly.

Why is this bad?

In some problem domains, it is good practice to avoid truncation. This lint can be activated to help assess where additional checks could be beneficial.

Example

fn as_u8(x: u64) -> u8 {
    x as u8
}

Use instead:

fn as_u8(x: u64) -> u8 {
    if let Ok(x) = u8::try_from(x) {
        x
    } else {
        todo!();
    }
}
// Or
#[allow(clippy::cast_possible_truncation)]
fn as_u16(x: u64) -> u16 {
    x as u16
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for casts from an unsigned type to a signed type of the same size, or possibly smaller due to target-dependent integers. Performing such a cast is a no-op for the compiler (that is, nothing is changed at the bit level), and the binary representation of the value is reinterpreted. This can cause wrapping if the value is too big for the target signed type. However, the cast works as defined, so this lint is Allow by default.

Why is this bad?

While such a cast is not bad in itself, the results can be surprising when this is not the intended behavior:

Example

let _ = u32::MAX as i32; // will yield a value of `-1`

Use instead:

let _ = i32::try_from(u32::MAX).ok();
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for casts from any numeric type to a float type where the receiving type cannot store all values from the original type without rounding errors. This possible rounding is to be expected, so this lint is Allow by default.

Basically, this warns on casting any integer with 32 or more bits to f32 or any 64-bit integer to f64.

Why is this bad?

It’s not bad at all. But in some applications it can be helpful to know where precision loss can take place. This lint can help find those places in the code.

Example

let x = u64::MAX;
x as f64;
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for casts, using as or pointer::cast, from a less strictly aligned pointer to a more strictly aligned pointer.

Why is this bad?

Dereferencing the resulting pointer may be undefined behavior.

Known problems

Using std::ptr::read_unaligned and std::ptr::write_unaligned or similar on the resulting pointer is fine. Is over-zealous: casts with manual alignment checks or casts like u64 -> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.

Example

let _ = (&1u8 as *const u8) as *const u16;
let _ = (&mut 1u8 as *mut u8) as *mut u16;

(&1u8 as *const u8).cast::<u16>();
(&mut 1u8 as *mut u8).cast::<u16>();
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for casts from a signed to an unsigned numeric type. In this case, negative values wrap around to large positive values, which can be quite surprising in practice. However, since the cast works as defined, this lint is Allow by default.

Why is this bad?

Possibly surprising results. You can activate this lint as a one-time check to see where numeric wrapping can arise.

Example

let y: i8 = -1;
y as u64; // will return 18446744073709551615
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of cfg that excludes code from test builds. (i.e., #[cfg(not(test))])

Why is this bad?

This may give the false impression that a codebase has 100% coverage, yet actually has untested code. Enabling this also guards against excessive mockery as well, which is an anti-pattern.

Example

#[cfg(not(test))]
important_check(); // I'm not actually tested, but not including me will falsely increase coverage!

Use instead:

important_check();
Applicability: Unspecified(?)
Added in: 1.81.0

What it does

Checks for explicit bounds checking when casting.

Why is this bad?

Reduces the readability of statements & is error prone.

Example

foo <= i32::MAX as u32;

Use instead:

i32::try_from(foo).is_ok();

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.37.0

What it does

Checks for usage of .drain(..) for the sole purpose of clearing a container.

Why is this bad?

This creates an unnecessary iterator that is dropped immediately.

Calling .clear() also makes the intent clearer.

Example

let mut v = vec![1, 2, 3];
v.drain(..);

Use instead:

let mut v = vec![1, 2, 3];
v.clear();
Applicability: MachineApplicable(?)
Added in: 1.70.0

What it does

Checks for usage of .clone() on a ref-counted pointer, (Rc, Arc, rc::Weak, or sync::Weak), and suggests calling Clone via unified function syntax instead (e.g., Rc::clone(foo)).

Why restrict this?

Calling .clone() on an Rc, Arc, or Weak can obscure the fact that only the pointer is being cloned, not the underlying data.

Example

let x = Rc::new(1);

x.clone();

Use instead:

Rc::clone(&x);
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of cloned() on an Iterator or Option where copied() could be used instead.

Why is this bad?

copied() is better because it guarantees that the type being cloned implements Copy.

Example

[1, 2, 3].iter().cloned();

Use instead:

[1, 2, 3].iter().copied();

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.53.0

What it does

Checks for methods with high cognitive complexity.

Why is this bad?

Methods of high cognitive complexity tend to be hard to both read and maintain. Also LLVM will tend to optimize small methods better.

Known problems

Sometimes it’s hard to find a way to reduce the complexity.

Example

You’ll see it when you get the warning.

Past names

  • cyclomatic_complexity

Configuration

  • cognitive-complexity-threshold: The maximum cognitive complexity a function can have

    (default: 25)

Applicability: Unspecified(?)
Added in: 1.35.0

What it does

Checks for collections that are never queried.

Why is this bad?

Putting effort into constructing a collection but then never querying it might indicate that the author forgot to do whatever they intended to do with the collection. Example: Clone a vector, sort it for iteration, but then mistakenly iterate the original vector instead.

Example

let mut sorted_samples = samples.clone();
sorted_samples.sort();
for sample in &samples { // Oops, meant to use `sorted_samples`.
    println!("{sample}");
}

Use instead:

let mut sorted_samples = samples.clone();
sorted_samples.sort();
for sample in &sorted_samples {
    println!("{sample}");
}
Applicability: Unspecified(?)
Added in: 1.70.0

What it does

Checks comparison chains written with if that can be rewritten with match and cmp.

Why is this bad?

if is not guaranteed to be exhaustive and conditionals can get repetitive

Known problems

The match statement may be slower due to the compiler not inlining the call to cmp. See issue #5354

Example

fn f(x: u8, y: u8) {
    if x > y {
        a()
    } else if x < y {
        b()
    } else {
        c()
    }
}

Use instead:

use std::cmp::Ordering;
fn f(x: u8, y: u8) {
     match x.cmp(&y) {
         Ordering::Greater => a(),
         Ordering::Less => b(),
         Ordering::Equal => c()
     }
}
Applicability: HasPlaceholders(?)
Added in: 1.40.0

What it does

Checks for types that implement Copy as well as Iterator.

Why is this bad?

Implicit copies can be confusing when working with iterator combinators.

Example

#[derive(Copy, Clone)]
struct Countdown(u8);

impl Iterator for Countdown {
    // ...
}

let a: Vec<_> = my_iterator.take(1).collect();
let b: Vec<_> = my_iterator.collect();
Applicability: Unspecified(?)
Added in: 1.30.0

What it does

Checks usage of std::fs::create_dir and suggest using std::fs::create_dir_all instead.

Why restrict this?

Sometimes std::fs::create_dir is mistakenly chosen over std::fs::create_dir_all, resulting in failure when more than one directory needs to be created or when the directory already exists. Crates which never need to specifically create a single directory may wish to prevent this mistake.

Example

std::fs::create_dir("foo");

Use instead:

std::fs::create_dir_all("foo");
Applicability: MaybeIncorrect(?)
Added in: 1.48.0

What it does

Checks for usage of the dbg! macro.

Why restrict this?

The dbg! macro is intended as a debugging tool. It should not be present in released software or committed to a version control system.

Example

dbg!(true)

Use instead:

true

Configuration

  • allow-dbg-in-tests: Whether dbg! should be allowed in test functions or #[cfg(test)]

    (default: false)

Applicability: MachineApplicable(?)
Added in: 1.34.0

What it does

Checks for function/method calls with a mutable parameter in debug_assert!, debug_assert_eq! and debug_assert_ne! macros.

Why is this bad?

In release builds debug_assert! macros are optimized out by the compiler. Therefore mutating something in a debug_assert! macro results in different behavior between a release and debug build.

Example

debug_assert_eq!(vec![3].pop(), Some(3));

// or

debug_assert!(takes_a_mut_parameter(&mut x));
Applicability: Unspecified(?)
Added in: 1.40.0

What it does

Warns if there is a better representation for a numeric literal.

Why restrict this?

Especially for big powers of 2, a hexadecimal representation is usually more readable than a decimal representation.

Example

`255` => `0xFF`
`65_535` => `0xFFFF`
`4_042_322_160` => `0xF0F0_F0F0`

Configuration

  • literal-representation-threshold: The lower bound for linting decimal literals

    (default: 16384)

Applicability: MaybeIncorrect(?)
Added in: pre 1.29.0

What it does

Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type inference.

Default numeric fallback means that if numeric types have not yet been bound to concrete types at the end of type inference, then integer type is bound to i32, and similarly floating type is bound to f64.

See RFC0212 for more information about the fallback.

Why restrict this?

To ensure that every numeric type is chosen explicitly rather than implicitly.

Known problems

This lint is implemented using a custom algorithm independent of rustc’s inference, which results in many false positives and false negatives.

Example

let i = 10;
let f = 1.23;

Use instead:

let i = 10_i32;
let f = 1.23_f64;
Applicability: MaybeIncorrect(?)
Added in: 1.52.0

What it does

Checks for literal calls to Default::default().

Why is this bad?

It’s easier for the reader if the name of the type is used, rather than the generic Default.

Example

let s: String = Default::default();

Use instead:

let s = String::default();
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Displays a warning when a union is declared with the default representation (without a #[repr(C)] attribute).

Why restrict this?

Unions in Rust have unspecified layout by default, despite many people thinking that they lay out each field at the start of the union (like C does). That is, there are no guarantees about the offset of the fields for unions with multiple non-ZST fields without an explicitly specified layout. These cases may lead to undefined behavior in unsafe blocks.

Example

union Foo {
    a: i32,
    b: u32,
}

fn main() {
    let _x: u32 = unsafe {
        Foo { a: 0_i32 }.b // Undefined behavior: `b` is allowed to be padding
    };
}

Use instead:

#[repr(C)]
union Foo {
    a: i32,
    b: u32,
}

fn main() {
    let _x: u32 = unsafe {
        Foo { a: 0_i32 }.b // Now defined behavior, this is just an i32 -> u32 transmute
    };
}
Applicability: Unspecified(?)
Added in: 1.60.0

What it does

Checks for slicing expressions which are equivalent to dereferencing the value.

Why restrict this?

Some people may prefer to dereference rather than slice.

Example

let vec = vec![1, 2, 3];
let slice = &vec[..];

Use instead:

let vec = vec![1, 2, 3];
let slice = &*vec;
Applicability: MachineApplicable(?)
Added in: 1.61.0

What it does

Checks for types that derive PartialEq and could implement Eq.

Why is this bad?

If a type T derives PartialEq and all of its members implement Eq, then T can always implement Eq. Implementing Eq allows T to be used in APIs that require Eq types. It also allows structs containing T to derive Eq themselves.

Example

#[derive(PartialEq)]
struct Foo {
    i_am_eq: i32,
    i_am_eq_too: Vec<String>,
}

Use instead:

#[derive(PartialEq, Eq)]
struct Foo {
    i_am_eq: i32,
    i_am_eq_too: Vec<String>,
}
Applicability: MachineApplicable(?)
Added in: 1.63.0

What it does

Checks for usage of unicode scripts other than those explicitly allowed by the lint config.

This lint doesn’t take into account non-text scripts such as Unknown and Linear_A. It also ignores the Common script type. While configuring, be sure to use official script name aliases from the list of supported scripts.

See also: non_ascii_idents.

Why restrict this?

It may be not desired to have many different scripts for identifiers in the codebase.

Note that if you only want to allow typical English, you might want to use built-in non_ascii_idents lint instead.

Example

// Assuming that `clippy.toml` contains the following line:
// allowed-scripts = ["Latin", "Cyrillic"]
let counter = 10; // OK, latin is allowed.
let счётчик = 10; // OK, cyrillic is allowed.
let zähler = 10; // OK, it's still latin.
let カウンタ = 10; // Will spawn the lint.

Configuration

  • allowed-scripts: The list of unicode scripts allowed to be used in the scope.

    (default: ["Latin"])

Applicability: Unspecified(?)
Added in: 1.55.0

What it does

Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (\).

Why is this bad?

Double spaces, when used as doc comment linebreaks, can be difficult to see, and may accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (\) is clearer in this regard.

Example

The two replacement dots in this example represent a double space.

/// This command takes two numbers as inputs and··
/// adds them together, and then returns the result.
fn add(l: i32, r: i32) -> i32 {
    l + r
}

Use instead:

/// This command takes two numbers as inputs and\
/// adds them together, and then returns the result.
fn add(l: i32, r: i32) -> i32 {
    l + r
}
Applicability: MachineApplicable(?)
Added in: 1.87.0

What it does

Checks if included files in doc comments are included only for cfg(doc).

Why restrict this?

These files are not useful for compilation but will still be included. Also, if any of these non-source code file is updated, it will trigger a recompilation.

Known problems

Excluding this will currently result in the file being left out if the item’s docs are inlined from another crate. This may be fixed in a future version of rustdoc.

Example

#![doc = include_str!("some_file.md")]

Use instead:

#![cfg_attr(doc, doc = include_str!("some_file.md"))]
Applicability: MachineApplicable(?)
Added in: 1.85.0

What it does

Checks for the presence of _, :: or camel-case words outside ticks in documentation.

Why is this bad?

Rustdoc supports markdown formatting, _, :: and camel-case probably indicates some code which should be included between ticks. _ can also be used for emphasis in markdown, this lint tries to consider that.

Known problems

Lots of bad docs won’t be fixed, what the lint checks for is limited, and there are still false positives. HTML elements and their content are not linted.

In addition, when writing documentation comments, including [] brackets inside a link text would trip the parser. Therefore, documenting link with [SmallVec<[T; INLINE_CAPACITY]>] and then [SmallVec<[T; INLINE_CAPACITY]>]: SmallVec would fail.

Examples

/// Do something with the foo_bar parameter. See also
/// that::other::module::foo.
// ^ `foo_bar` and `that::other::module::foo` should be ticked.
fn doit(foo_bar: usize) {}
// Link text with `[]` brackets should be written as following:
/// Consume the array and return the inner
/// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
/// [SmallVec]: SmallVec
fn main() {}

Configuration

  • doc-valid-idents: The list of words this lint should not consider as identifiers needing ticks. The value ".." can be used as part of the list to indicate, that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value. For example:
  • doc-valid-idents = ["ClipPy"] would replace the default list with ["ClipPy"].

  • doc-valid-idents = ["ClipPy", ".."] would append ClipPy to the default list.

    (default: ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"])

Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for lifetime annotations which can be replaced with anonymous lifetimes ('_).

Why is this bad?

The additional lifetimes can make the code look more complicated.

Known problems

This lint ignores functions with where clauses that reference lifetimes to prevent false positives.

Example

fn f<'a>(x: &'a str) -> Chars<'a> {
    x.chars()
}

Use instead:

fn f(x: &str) -> Chars<'_> {
    x.chars()
}
Applicability: MachineApplicable(?)
Added in: 1.84.0

What it does

Checks for usage of if expressions with an else if branch, but without a final else branch.

Why restrict this?

Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).

Example

if x.is_positive() {
    a();
} else if x.is_negative() {
    b();
}

Use instead:

if x.is_positive() {
    a();
} else if x.is_negative() {
    b();
} else {
    // We don't care about zero.
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for empty Drop implementations.

Why restrict this?

Empty Drop implementations have no effect when dropping an instance of the type. They are most likely useless. However, an empty Drop implementation prevents a type from being destructured, which might be the intention behind adding the implementation as a marker.

Example

struct S;

impl Drop for S {
    fn drop(&mut self) {}
}

Use instead:

struct S;
Applicability: MaybeIncorrect(?)
Added in: 1.62.0

What it does

Checks for enums with no variants, which therefore are uninhabited types (cannot be instantiated).

As of this writing, the never_type is still a nightly-only experimental API. Therefore, this lint is only triggered if #![feature(never_type)] is enabled.

Why is this bad?

  • If you only want a type which can’t be instantiated, you should use ! (the primitive type “never”), because ! has more extensive compiler support (type inference, etc.) and implementations of common traits.

  • If you need to introduce a distinct type, consider using a newtype struct containing ! instead (struct MyType(pub !)), because it is more idiomatic to use a struct rather than an enum when an enum is unnecessary.

    If you do this, note that the visibility of the ! field determines whether the uninhabitedness is visible in documentation, and whether it can be pattern matched to mark code unreachable. If the field is not visible, then the struct acts like any other struct with private fields.

  • If the enum has no variants only because all variants happen to be disabled by conditional compilation, then it would be appropriate to allow the lint, with #[allow(empty_enum)].

For further information, visit the never type’s documentation.

Example

enum CannotExist {}

Use instead:

#![feature(never_type)]

/// Use the `!` type directly...
type CannotExist = !;

/// ...or define a newtype which is distinct.
struct CannotExist2(pub !);
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Finds enum variants without fields that are declared with empty brackets.

Why restrict this?

Empty brackets after a enum variant declaration are redundant and can be omitted, and it may be desirable to do so consistently for style.

However, removing the brackets also introduces a public constant named after the variant, so this is not just a syntactic simplification but an API change, and adding them back is a breaking API change.

Example

enum MyEnum {
    HasData(u8),
    HasNoData(),       // redundant parentheses
    NoneHereEither {}, // redundant braces
}

Use instead:

enum MyEnum {
    HasData(u8),
    HasNoData,
    NoneHereEither,
}
Applicability: MaybeIncorrect(?)
Added in: 1.77.0

What it does

Finds structs without fields (a so-called “empty struct”) that are declared with brackets.

Why restrict this?

Empty brackets after a struct declaration can be omitted, and it may be desirable to do so consistently for style.

However, removing the brackets also introduces a public constant named after the struct, so this is not just a syntactic simplification but an API change, and adding them back is a breaking API change.

Example

struct Cookie {}
struct Biscuit();

Use instead:

struct Cookie;
struct Biscuit;
Applicability: Unspecified(?)
Added in: 1.62.0

What it does

Checks for use Enum::*.

Why is this bad?

It is usually better style to use the prefixed name of an enumeration variant, rather than importing variants.

Known problems

Old-style enumerations that prefix the variants are still around.

Example

use std::cmp::Ordering::*;

foo(Less);

Use instead:

use std::cmp::Ordering;

foo(Ordering::Less)
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for pattern matchings that can be expressed using equality.

Why is this bad?

  • It reads better and has less cognitive load because equality won’t cause binding.
  • It is a Yoda condition. Yoda conditions are widely criticized for increasing the cognitive load of reading the code.
  • Equality is a simple bool expression and can be merged with && and || and reuse if blocks

Example

if let Some(2) = x {
    do_thing();
}

Use instead:

if x == Some(2) {
    do_thing();
}
Applicability: MachineApplicable(?)
Added in: 1.57.0

What it does

Checks for types named Error that implement Error.

Why restrict this?

It can become confusing when a codebase has 20 types all named Error, requiring either aliasing them in the use statement or qualifying them like my_module::Error. This hinders comprehension, as it requires you to memorize every variation of importing Error used across a codebase.

Example

#[derive(Debug)]
pub enum Error { ... }

impl std::fmt::Display for Error { ... }

impl std::error::Error for Error { ... }
Applicability: Unspecified(?)
Added in: 1.73.0

What it does

Warns on any exported enums that are not tagged #[non_exhaustive]

Why restrict this?

Making an enum exhaustive is a stability commitment: adding a variant is a breaking change. A project may wish to ensure that there are no exhaustive enums or that every exhaustive enum is explicitly #[allow]ed.

Example

enum Foo {
    Bar,
    Baz
}

Use instead:

#[non_exhaustive]
enum Foo {
    Bar,
    Baz
}
Applicability: MaybeIncorrect(?)
Added in: 1.51.0

What it does

Warns on any exported structs that are not tagged #[non_exhaustive]

Why restrict this?

Making a struct exhaustive is a stability commitment: adding a field is a breaking change. A project may wish to ensure that there are no exhaustive structs or that every exhaustive struct is explicitly #[allow]ed.

Example

struct Foo {
    bar: u8,
    baz: String,
}

Use instead:

#[non_exhaustive]
struct Foo {
    bar: u8,
    baz: String,
}
Applicability: MaybeIncorrect(?)
Added in: 1.51.0

What it does

Detects calls to the exit() function which terminates the program.

Why restrict this?

exit() immediately terminates the program with no information other than an exit code. This provides no means to troubleshoot a problem, and may be an unexpected side effect.

Codebases may use this lint to require that all exits are performed either by panicking (which produces a message, a code location, and optionally a backtrace) or by returning from main() (which is a single place to look).

Example

std::process::exit(0)

Use instead:

// To provide a stacktrace and additional information
panic!("message");

// or a main method with a return
fn main() -> Result<(), i32> {
    Ok(())
}
Applicability: Unspecified(?)
Added in: 1.41.0

What it does

Checks for .expect() or .expect_err() calls on Results and .expect() call on Options.

Why restrict this?

Usually it is better to handle the None or Err case. Still, for a lot of quick-and-dirty code, expect is a good choice, which is why this lint is Allow by default.

result.expect() will let the thread panic on Err values. Normally, you want to implement more sophisticated error handling, and propagate errors upwards with ? operator.

Examples

option.expect("one");
result.expect("one");

Use instead:

option?;

// or

result?;

Past names

  • option_expect_used
  • result_expect_used

Configuration

  • allow-expect-in-consts: Whether expect should be allowed in code always evaluated at compile time

    (default: true)

  • allow-expect-in-tests: Whether expect should be allowed in test functions or #[cfg(test)]

    (default: false)

Applicability: Unspecified(?)
Added in: 1.45.0

What it does

Checks for explicit Clone implementations for Copy types.

Why is this bad?

To avoid surprising behavior, these traits should agree and the behavior of Copy cannot be overridden. In almost all situations a Copy type should have a Clone implementation that does nothing more than copy the object, which is what #[derive(Copy, Clone)] gets you.

Example

#[derive(Copy)]
struct Foo;

impl Clone for Foo {
    // ..
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for explicit deref() or deref_mut() method calls.

Why is this bad?

Dereferencing by &*x or &mut *x is clearer and more concise, when not part of a method chain.

Example

use std::ops::Deref;
let a: &mut String = &mut String::from("foo");
let b: &str = a.deref();

Use instead:

let a: &mut String = &mut String::from("foo");
let b = &*a;

This lint excludes all of:

let _ = d.unwrap().deref();
let _ = Foo::deref(&foo);
let _ = <Foo as Deref>::deref(&foo);
Applicability: MachineApplicable(?)
Added in: 1.44.0

What it does

Checks for loops on y.into_iter() where y will do, and suggests the latter.

Why is this bad?

Readability.

Example

// with `y` a `Vec` or slice:
for x in y.into_iter() {
    // ..
}

can be rewritten to

for x in y {
    // ..
}
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for loops on x.iter() where &x will do, and suggests the latter.

Why is this bad?

Readability.

Known problems

False negatives. We currently only warn on some known types.

Example

// with `y` a `Vec` or slice:
for x in y.iter() {
    // ..
}

Use instead:

for x in &y {
    // ..
}

Configuration

  • enforce-iter-loop-reborrow: Whether to recommend using implicit into iter for reborrowed values.

Example

let mut vec = vec![1, 2, 3];
let rmvec = &mut vec;
for _ in rmvec.iter() {}
for _ in rmvec.iter_mut() {}

Use instead:

let mut vec = vec![1, 2, 3];
let rmvec = &mut vec;
for _ in &*rmvec {}
for _ in &mut *rmvec {}

(default: false)

Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for impls of From<..> that contain panic!() or unwrap()

Why is this bad?

TryFrom should be used if there’s a possibility of failure.

Example

struct Foo(i32);

impl From<String> for Foo {
    fn from(s: String) -> Self {
        Foo(s.parse().unwrap())
    }
}

Use instead:

struct Foo(i32);

impl TryFrom<String> for Foo {
    type Error = ();
    fn try_from(s: String) -> Result<Self, Self::Error> {
        if let Ok(parsed) = s.parse() {
            Ok(Foo(parsed))
        } else {
            Err(())
        }
    }
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of scoped visibility modifiers, like pub(crate), on fields. These make a field visible within a scope between public and private.

Why restrict this?

Scoped visibility modifiers cause a field to be accessible within some scope between public and private, potentially within an entire crate. This allows for fields to be non-private while upholding internal invariants, but can be a code smell. Scoped visibility requires checking a greater area, potentially an entire crate, to verify that an invariant is upheld, and global analysis requires a lot of effort.

Example

pub mod public_module {
    struct MyStruct {
        pub(crate) first_field: bool,
        pub(super) second_field: bool
    }
}

Use instead:

pub mod public_module {
    struct MyStruct {
        first_field: bool,
        second_field: bool
    }
    impl MyStruct {
        pub(crate) fn get_first_field(&self) -> bool {
            self.first_field
        }
        pub(super) fn get_second_field(&self) -> bool {
            self.second_field
        }
    }
}
Applicability: Unspecified(?)
Added in: 1.81.0

What it does

Checks for FileType::is_file().

Why restrict this?

When people testing a file type with FileType::is_file they are testing whether a path is something they can get bytes from. But is_file doesn’t cover special file types in unix-like systems, and doesn’t cover symlink in windows. Using !FileType::is_dir() is a better way to that intention.

Example

let metadata = std::fs::metadata("foo.txt")?;
let filetype = metadata.file_type();

if filetype.is_file() {
    // read file
}

should be written as:

let metadata = std::fs::metadata("foo.txt")?;
let filetype = metadata.file_type();

if !filetype.is_dir() {
    // read file
}
Applicability: Unspecified(?)
Added in: 1.42.0

What it does

Checks for usage of _.filter_map(_).next().

Why is this bad?

Readability, this can be written more concisely as _.find_map(_).

Example

 (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();

Can be written as

 (0..3).find_map(|x| if x == 2 { Some(x) } else { None });

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.36.0

What it does

Checks for usage of Iterator::flat_map() where filter_map() could be used instead.

Why is this bad?

filter_map() is known to always produce 0 or 1 output items per input item, rather than however many the inner iterator type produces. Therefore, it maintains the upper bound in Iterator::size_hint(), and communicates to the reader that the input items are not being expanded into multiple output items without their having to notice that the mapping function returns an Option.

Example

let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();

Use instead:

let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
Applicability: MachineApplicable(?)
Added in: 1.53.0

What it does

Checks for float arithmetic.

Why restrict this?

For some embedded systems or kernel development, it can be useful to rule out floating-point numbers.

Example

a + 1.0;
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called *eq* (which probably implement equality for a type involving floats).

Why is this bad?

Floating point calculations are usually imprecise, so asking if two values are exactly equal is asking for trouble because arriving at the same logical result via different routes (e.g. calculation versus constant) may yield different values.

Example

let a: f64 = 1000.1;
let b: f64 = 0.2;
let x = a + b;
let y = 1000.3; // Expected value.

// Actual value: 1000.3000000000001
println!("{x}");

let are_equal = x == y;
println!("{are_equal}"); // false

The correct way to compare floating point numbers is to define an allowed error margin. This may be challenging if there is no “natural” error margin to permit. Broadly speaking, there are two cases:

  1. If your values are in a known range and you can define a threshold for “close enough to be equal”, it may be appropriate to define an absolute error margin. For example, if your data is “length of vehicle in centimeters”, you may consider 0.1 cm to be “close enough”.
  2. If your code is more general and you do not know the range of values, you should use a relative error margin, accepting e.g. 0.1% of error regardless of specific values.

For the scenario where you can define a meaningful absolute error margin, consider using:

let a: f64 = 1000.1;
let b: f64 = 0.2;
let x = a + b;
let y = 1000.3; // Expected value.

const ALLOWED_ERROR_VEHICLE_LENGTH_CM: f64 = 0.1;
let within_tolerance = (x - y).abs() < ALLOWED_ERROR_VEHICLE_LENGTH_CM;
println!("{within_tolerance}"); // true

NB! Do not use f64::EPSILON - while the error margin is often called “epsilon”, this is a different use of the term that is not suitable for floating point equality comparison. Indeed, for the example above using f64::EPSILON as the allowed error would return false.

For the scenario where no meaningful absolute error can be defined, refer to the floating point guide for a reference implementation of relative error based comparison of floating point values. MIN_NORMAL in the reference implementation is equivalent to MIN_POSITIVE in Rust.

Applicability: HasPlaceholders(?)
Added in: pre 1.29.0

What it does

Checks for (in-)equality comparisons on constant floating-point values (apart from zero), except in functions called *eq* (which probably implement equality for a type involving floats).

Why restrict this?

Floating point calculations are usually imprecise, so asking if two values are exactly equal is asking for trouble because arriving at the same logical result via different routes (e.g. calculation versus constant) may yield different values.

Example

let a: f64 = 1000.1;
let b: f64 = 0.2;
let x = a + b;
const Y: f64 = 1000.3; // Expected value.

// Actual value: 1000.3000000000001
println!("{x}");

let are_equal = x == Y;
println!("{are_equal}"); // false

The correct way to compare floating point numbers is to define an allowed error margin. This may be challenging if there is no “natural” error margin to permit. Broadly speaking, there are two cases:

  1. If your values are in a known range and you can define a threshold for “close enough to be equal”, it may be appropriate to define an absolute error margin. For example, if your data is “length of vehicle in centimeters”, you may consider 0.1 cm to be “close enough”.
  2. If your code is more general and you do not know the range of values, you should use a relative error margin, accepting e.g. 0.1% of error regardless of specific values.

For the scenario where you can define a meaningful absolute error margin, consider using:

let a: f64 = 1000.1;
let b: f64 = 0.2;
let x = a + b;
const Y: f64 = 1000.3; // Expected value.

const ALLOWED_ERROR_VEHICLE_LENGTH_CM: f64 = 0.1;
let within_tolerance = (x - Y).abs() < ALLOWED_ERROR_VEHICLE_LENGTH_CM;
println!("{within_tolerance}"); // true

NB! Do not use f64::EPSILON - while the error margin is often called “epsilon”, this is a different use of the term that is not suitable for floating point equality comparison. Indeed, for the example above using f64::EPSILON as the allowed error would return false.

For the scenario where no meaningful absolute error can be defined, refer to the floating point guide for a reference implementation of relative error based comparison of floating point values. MIN_NORMAL in the reference implementation is equivalent to MIN_POSITIVE in Rust.

Applicability: HasPlaceholders(?)
Added in: pre 1.29.0

What it does

Checks for excessive use of bools in function definitions.

Why is this bad?

Calls to such functions are confusing and error prone, because it’s hard to remember argument order and you have no type system support to back you up. Using two-variant enums instead of bools often makes API easier to use.

Example

fn f(is_round: bool, is_hot: bool) { ... }

Use instead:

enum Shape {
    Round,
    Spiky,
}

enum Temperature {
    Hot,
    IceCold,
}

fn f(shape: Shape, temperature: Temperature) { ... }

Configuration

  • max-fn-params-bools: The maximum number of bool parameters a function can have

    (default: 3)

Applicability: Unspecified(?)
Added in: 1.43.0

What it does

Checks for casts of a function pointer to any integer type.

Why restrict this?

Casting a function pointer to an integer can have surprising results and can occur accidentally if parentheses are omitted from a function call. If you aren’t doing anything low-level with function pointers then you can opt out of casting functions to integers in order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function pointer casts in your code.

Example

// fn1 is cast as `usize`
fn fn1() -> u16 {
    1
};
let _ = fn1 as usize;

Use instead:

// maybe you intended to call the function?
fn fn2() -> u16 {
    1
};
let _ = fn2() as usize;

// or

// maybe you intended to cast it to a function type?
fn fn3() -> u16 {
    1
}
let _ = fn3 as fn() -> u16;
Applicability: MaybeIncorrect(?)
Added in: 1.58.0

What it does

Checks for usage of .map(|_| format!(..)).collect::<String>().

Why is this bad?

This allocates a new string for every element in the iterator. This can be done more efficiently by creating the String once and appending to it in Iterator::fold, using either the write! macro which supports exactly the same syntax as the format! macro, or concatenating with + in case the iterator yields &str/String.

Note also that write!-ing into a String can never fail, despite the return type of write! being std::fmt::Result, so it can be safely ignored or unwrapped.

Example

fn hex_encode(bytes: &[u8]) -> String {
    bytes.iter().map(|b| format!("{b:02X}")).collect()
}

Use instead:

use std::fmt::Write;
fn hex_encode(bytes: &[u8]) -> String {
    bytes.iter().fold(String::new(), |mut output, b| {
        let _ = write!(output, "{b:02X}");
        output
    })
}
Applicability: Unspecified(?)
Added in: 1.73.0

What it does

Detects cases where the result of a format! call is appended to an existing String.

Why is this bad?

Introduces an extra, avoidable heap allocation.

Known problems

format! returns a String but write! returns a Result. Thus you are forced to ignore the Err variant to achieve the same API.

While using write! in the suggested way should never fail, this isn’t necessarily clear to the programmer.

Example

let mut s = String::new();
s += &format!("0x{:X}", 1024);
s.push_str(&format!("0x{:X}", 1024));

Use instead:

use std::fmt::Write as _; // import without risk of name clashing

let mut s = String::new();
let _ = write!(s, "0x{:X}", 1024);
Applicability: Unspecified(?)
Added in: 1.62.0

What it does

Checks for from_iter() function calls on types that implement the FromIterator trait.

Why is this bad?

If it’s needed to create a collection from the contents of an iterator, the Iterator::collect(_) method is preferred. However, when it’s needed to specify the container type, Vec::from_iter(_) can be more readable than using a turbofish (e.g. _.collect::<Vec<_>>()). See FromIterator documentation

Example

let five_fives = std::iter::repeat(5).take(5);

let v = Vec::from_iter(five_fives);

assert_eq!(v, vec![5, 5, 5, 5, 5]);

Use instead:

let five_fives = std::iter::repeat(5).take(5);

let v: Vec<i32> = five_fives.collect();

assert_eq!(v, vec![5, 5, 5, 5, 5]);

but prefer to use

let numbers: Vec<i32> = FromIterator::from_iter(1..=5);

instead of

let numbers = (1..=5).collect::<Vec<_>>();
Applicability: MaybeIncorrect(?)
Added in: 1.49.0

What it does

This lint requires Future implementations returned from functions and methods to implement the Send marker trait, ignoring type parameters.

If a function is generic and its Future conditionally implements Send based on a generic parameter then it is considered Send and no warning is emitted.

This can be used by library authors (public and internal) to ensure their functions are compatible with both multi-threaded runtimes that require Send futures, as well as single-threaded runtimes where callers may choose !Send types for generic parameters.

Why is this bad?

A Future implementation captures some state that it needs to eventually produce its final value. When targeting a multithreaded executor (which is the norm on non-embedded devices) this means that this state may need to be transported to other threads, in other words the whole Future needs to implement the Send marker trait. If it does not, then the resulting Future cannot be submitted to a thread pool in the end user’s code.

Especially for generic functions it can be confusing to leave the discovery of this problem to the end user: the reported error location will be far from its cause and can in many cases not even be fixed without modifying the library where the offending Future implementation is produced.

Example

async fn not_send(bytes: std::rc::Rc<[u8]>) {}

Use instead:

async fn is_send(bytes: std::sync::Arc<[u8]>) {}
Applicability: Unspecified(?)
Added in: 1.44.0

What it does

Checks for usage of .get().unwrap() (or .get_mut().unwrap) on a standard library type which implements Index

Why restrict this?

Using the Index trait ([]) is more clear and more concise.

Known problems

Not a replacement for error handling: Using either .unwrap() or the Index trait ([]) carries the risk of causing a panic if the value being accessed is None. If the use of .get().unwrap() is a temporary placeholder for dealing with the Option type, then this does not mitigate the need for error handling. If there is a chance that .get() will be None in your program, then it is advisable that the None case is handled in a future refactor instead of using .unwrap() or the Index trait.

Example

let mut some_vec = vec![0, 1, 2, 3];
let last = some_vec.get(3).unwrap();
*some_vec.get_mut(0).unwrap() = 1;

The correct use would be:

let mut some_vec = vec![0, 1, 2, 3];
let last = some_vec[3];
some_vec[0] = 1;
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for the usage of the to_ne_bytes method and/or the function from_ne_bytes.

Why restrict this?

To ensure use of explicitly chosen endianness rather than the target’s endianness, such as when implementing network protocols or file formats rather than FFI.

Example

let _x = 2i32.to_ne_bytes();
let _y = 2i64.to_ne_bytes();
Applicability: Unspecified(?)
Added in: 1.72.0

What it does

Checks for usage of ! or != in an if condition with an else branch.

Why is this bad?

Negations reduce the readability of statements.

Example

if !v.is_empty() {
    a()
} else {
    b()
}

Could be written:

if v.is_empty() {
    b()
} else {
    a()
}
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for if-else that could be written using either bool::then or bool::then_some.

Why restrict this?

Looks a little redundant. Using bool::then is more concise and incurs no loss of clarity. For simple calculations and known values, use bool::then_some, which is eagerly evaluated in comparison to bool::then.

Example

let a = if v.is_empty() {
    println!("true!");
    Some(42)
} else {
    None
};

Could be written:

let a = v.is_empty().then(|| {
    println!("true!");
    42
});

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.53.0

What it does

Checks for ignored tests without messages.

Why is this bad?

The reason for ignoring the test may not be obvious.

Example

#[test]
#[ignore]
fn test() {}

Use instead:

#[test]
#[ignore = "Some good reason"]
fn test() {}
Applicability: Unspecified(?)
Added in: 1.85.0

What it does

Checks for usage of _ in patterns of type ().

Why is this bad?

Matching with () explicitly instead of _ outlines the fact that the pattern contains no data. Also it would detect a type change that _ would ignore.

Example

match std::fs::create_dir("tmp-work-dir") {
    Ok(_) => println!("Working directory created"),
    Err(s) => eprintln!("Could not create directory: {s}"),
}

Use instead:

match std::fs::create_dir("tmp-work-dir") {
    Ok(()) => println!("Working directory created"),
    Err(s) => eprintln!("Could not create directory: {s}"),
}
Applicability: MachineApplicable(?)
Added in: 1.73.0

What it does

Lints when impl Trait is being used in a function’s parameters.

Why restrict this?

Turbofish syntax (::<>) cannot be used to specify the type of an impl Trait parameter, making impl Trait less powerful. Readability may also be a factor.

Example

trait MyTrait {}
fn foo(a: impl MyTrait) {
	// [...]
}

Use instead:

trait MyTrait {}
fn foo<T: MyTrait>(a: T) {
	// [...]
}
Applicability: HasPlaceholders(?)
Added in: 1.69.0

What it does

Checks for the usage of _.to_owned(), vec.to_vec(), or similar when calling _.clone() would be clearer.

Why is this bad?

These methods do the same thing as _.clone() but may be confusing as to why we are calling to_vec on something that is already a Vec or calling to_owned on something that is already owned.

Example

let a = vec![1, 2, 3];
let b = a.to_vec();
let c = a.to_owned();

Use instead:

let a = vec![1, 2, 3];
let b = a.clone();
let c = a.clone();
Applicability: MachineApplicable(?)
Added in: 1.52.0

What it does

Checks for public impl or fn missing generalization over different hashers and implicitly defaulting to the default hashing algorithm (SipHash).

Why is this bad?

HashMap or HashSet with custom hashers cannot be used with them.

Known problems

Suggestions for replacing constructors can contain false-positives. Also applying suggestions can require modification of other pieces of code, possibly including external crates.

Example

impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }

pub fn foo(map: &mut HashMap<i32, i32>) { }

could be rewritten as

impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { }

pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
Applicability: MaybeIncorrect(?)
Added in: pre 1.29.0

What it does

Checks for missing return statements at the end of a block.

Why restrict this?

Omitting the return keyword whenever possible is idiomatic Rust code, but:

  • Programmers coming from other languages might prefer the expressiveness of return.
  • It’s possible to miss the last returning statement because the only difference is a missing ;.
  • Especially in bigger code with multiple return paths, having a return keyword makes it easier to find the corresponding statements.

Example

fn foo(x: usize) -> usize {
    x
}

add return

fn foo(x: usize) -> usize {
    return x;
}
Applicability: MachineApplicable(?)
Added in: 1.33.0

What it does

Looks for floating-point expressions that can be expressed using built-in methods to improve accuracy at the cost of performance.

Why is this bad?

Negatively impacts accuracy.

Example

let a = 3f32;
let _ = a.powf(1.0 / 3.0);
let _ = (1.0 + a).ln();
let _ = a.exp() - 1.0;

Use instead:

let a = 3f32;
let _ = a.cbrt();
let _ = a.ln_1p();
let _ = a.exp_m1();
Applicability: MachineApplicable(?)
Added in: 1.43.0

What it does

Checks for struct constructors where the order of the field init in the constructor is inconsistent with the order in the struct definition.

Why is this bad?

Since the order of fields in a constructor doesn’t affect the resulted instance as the below example indicates,

#[derive(Debug, PartialEq, Eq)]
struct Foo {
    x: i32,
    y: i32,
}
let x = 1;
let y = 2;

// This assertion never fails:
assert_eq!(Foo { x, y }, Foo { y, x });

inconsistent order can be confusing and decreases readability and consistency.

Example

struct Foo {
    x: i32,
    y: i32,
}
let x = 1;
let y = 2;

Foo { y, x };

Use instead:

Foo { x, y };

Configuration

  • check-inconsistent-struct-field-initializers: Whether to suggest reordering constructor fields when initializers are present.

Warnings produced by this configuration aren’t necessarily fixed by just reordering the fields. Even if the suggested code would compile, it can change semantics if the initializer expressions have side effects. The following example from rust-clippy#11846 shows how the suggestion can run into borrow check errors:

struct MyStruct {
    vector: Vec<u32>,
    length: usize
}
fn main() {
    let vector = vec![1,2,3];
    MyStruct { length: vector.len(), vector};
}

(default: false)

Applicability: MachineApplicable(?)
Added in: 1.52.0

What it does

The lint checks for slice bindings in patterns that are only used to access individual slice values.

Why is this bad?

Accessing slice values using indices can lead to panics. Using refutable patterns can avoid these. Binding to individual values also improves the readability as they can be named.

Limitations

This lint currently only checks for immutable access inside if let patterns.

Example

let slice: Option<&[u32]> = Some(&[1, 2, 3]);

if let Some(slice) = slice {
    println!("{}", slice[0]);
}

Use instead:

let slice: Option<&[u32]> = Some(&[1, 2, 3]);

if let Some(&[first, ..]) = slice {
    println!("{}", first);
}

Configuration

  • max-suggested-slice-pattern-length: When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. For example, [_, _, _, e, ..] is a slice pattern with 4 elements.

    (default: 3)

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MaybeIncorrect(?)
Added in: 1.59.0

What it does

Checks for usage of indexing or slicing that may panic at runtime.

This lint does not report on indexing or slicing operations that always panic, clippy’s out_of_bound_indexing already handles those cases.

Why restrict this?

To avoid implicit panics from indexing and slicing.

There are “checked” alternatives which do not panic, and can be used with unwrap() to make an explicit panic when it is desired.

Limitations

This lint does not check for the usage of indexing or slicing on strings. These are covered by the more specific string_slice lint.

Example

// Vector
let x = vec![0, 1, 2, 3];

x[2];
x[100];
&x[2..100];

// Array
let y = [0, 1, 2, 3];

let i = 10; // Could be a runtime value
let j = 20;
&y[i..j];

Use instead:

x.get(2);
x.get(100);
x.get(2..100);

let i = 10;
let j = 20;
y.get(i..j);

Configuration

  • allow-indexing-slicing-in-tests: Whether indexing_slicing should be allowed in test functions or #[cfg(test)]

    (default: false)

  • suppress-restriction-lint-in-const: Whether to suppress a restriction lint in constant code. In same cases the restructured operation might not be unavoidable, as the suggested counterparts are unavailable in constant code. This configuration will cause restriction lints to trigger even if no suggestion can be made.

    (default: false)

Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of .to_string() on an &&T where T implements ToString directly (like &&str or &&String).

Why is this bad?

This bypasses the specialized implementation of ToString and instead goes through the more expensive string formatting facilities.

Example

// Generic implementation for `T: Display` is used (slow)
["foo", "bar"].iter().map(|s| s.to_string());

// OK, the specialized impl is used
["foo", "bar"].iter().map(|&s| s.to_string());
Applicability: MachineApplicable(?)
Added in: 1.40.0

What it does

Checks for infinite loops in a function where the return type is not ! and lint accordingly.

Why restrict this?

Making the return type ! serves as documentation that the function does not return. If the function is not intended to loop infinitely, then this lint may detect a bug.

Example

fn run_forever() {
    loop {
        // do something
    }
}

If infinite loops are as intended:

fn run_forever() -> ! {
    loop {
        // do something
    }
}

Otherwise add a break or return condition:

fn run_forever() {
    loop {
        // do something
        if condition {
            break;
        }
    }
}
Applicability: MaybeIncorrect(?)
Added in: 1.76.0

What it does

Checks for items annotated with #[inline(always)], unless the annotated function is empty or simply panics.

Why is this bad?

While there are valid uses of this annotation (and once you know when to use it, by all means allow this lint), it’s a common newbie-mistake to pepper one’s code with it.

As a rule of thumb, before slapping #[inline(always)] on a function, measure if that additional function call really affects your runtime profile sufficiently to make up for the increase in compile time.

Known problems

False positives, big time. This lint is meant to be deactivated by everyone doing serious performance work. This means having done the measurement.

Example

#[inline(always)]
fn not_quite_hot_code(..) { ... }
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of AT&T x86 assembly syntax.

Why restrict this?

To enforce consistent use of Intel x86 assembly syntax.

Example

asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));

Use instead:

asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
Applicability: Unspecified(?)
Added in: 1.49.0

What it does

Checks for usage of Intel x86 assembly syntax.

Why restrict this?

To enforce consistent use of AT&T x86 assembly syntax.

Example

asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);

Use instead:

asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
Applicability: Unspecified(?)
Added in: 1.49.0

What it does

Checks for division of integers

Why restrict this?

When outside of some very specific algorithms, integer division is very often a mistake because it discards the remainder.

Example

let x = 3 / 2;
println!("{}", x);

Use instead:

let x = 3f32 / 2f32;
println!("{}", x);
Applicability: Unspecified(?)
Added in: 1.37.0

What it does

Checks for the usage of division (/) and remainder (%) operations when performed on any integer types using the default Div and Rem trait implementations.

Why restrict this?

In cryptographic contexts, division can result in timing sidechannel vulnerabilities, and needs to be replaced with constant-time code instead (e.g. Barrett reduction).

Example

let my_div = 10 / 2;

Use instead:

let my_div = 10 >> 1;
Applicability: Unspecified(?)
Added in: 1.79.0

What it does

This is the opposite of the iter_without_into_iter lint. It looks for IntoIterator for (&|&mut) Type implementations without an inherent iter or iter_mut method on the type or on any of the types in its Deref chain.

Why is this bad?

It’s not bad, but having them is idiomatic and allows the type to be used in iterator chains by just calling .iter(), instead of the more awkward <&Type>::into_iter or (&val).into_iter() syntax in case of ambiguity with another IntoIterator impl.

Limitations

This lint focuses on providing an idiomatic API. Therefore, it will only lint on types which are accessible outside of the crate. For internal types, these methods can be added on demand if they are actually needed. Otherwise, it would trigger the dead_code lint for the unused method.

Example

struct MySlice<'a>(&'a [u8]);
impl<'a> IntoIterator for &MySlice<'a> {
    type Item = &'a u8;
    type IntoIter = std::slice::Iter<'a, u8>;
    fn into_iter(self) -> Self::IntoIter {
        self.0.iter()
    }
}

Use instead:

struct MySlice<'a>(&'a [u8]);
impl<'a> MySlice<'a> {
    pub fn iter(&self) -> std::slice::Iter<'a, u8> {
        self.into_iter()
    }
}
impl<'a> IntoIterator for &MySlice<'a> {
    type Item = &'a u8;
    type IntoIter = std::slice::Iter<'a, u8>;
    fn into_iter(self) -> Self::IntoIter {
        self.0.iter()
    }
}
Applicability: Unspecified(?)
Added in: 1.75.0

What it does

Checks for comparisons where the relation is always either true or false, but where one side has been upcast so that the comparison is necessary. Only integer types are checked.

Why is this bad?

An expression like let x : u8 = ...; (x as u32) > 300 will mistakenly imply that it is possible for x to be outside the range of u8.

Example

let x: u8 = 1;
(x as u32) > 300;
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for items declared after some statement in a block.

Why is this bad?

Items live for the entire scope they are declared in. But statements are processed in order. This might cause confusion as it’s hard to figure out which item is meant in a statement.

Example

fn foo() {
    println!("cake");
}

fn main() {
    foo(); // prints "foo"
    fn foo() {
        println!("foo");
    }
    foo(); // prints "foo"
}

Use instead:

fn foo() {
    println!("cake");
}

fn main() {
    fn foo() {
        println!("foo");
    }
    foo(); // prints "foo"
    foo(); // prints "foo"
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of .filter(Result::is_ok) that may be replaced with a .flatten() call. This lint will require additional changes to the follow-up calls as it affects the type.

Why is this bad?

This pattern is often followed by manual unwrapping of Result. The simplification results in more readable and succinct code without the need for manual unwrapping.

Example

vec![Ok::<i32, String>(1)].into_iter().filter(Result::is_ok);

Use instead:

vec![Ok::<i32, String>(1)].into_iter().flatten();
Applicability: HasPlaceholders(?)
Added in: 1.77.0

What it does

Checks for usage of .filter(Option::is_some) that may be replaced with a .flatten() call. This lint will require additional changes to the follow-up calls as it affects the type.

Why is this bad?

This pattern is often followed by manual unwrapping of the Option. The simplification results in more readable and succinct code without the need for manual unwrapping.

Example

vec![Some(1)].into_iter().filter(Option::is_some);

Use instead:

vec![Some(1)].into_iter().flatten();
Applicability: HasPlaceholders(?)
Added in: 1.77.0

What it does

Detects methods named iter or iter_mut that do not have a return type that implements Iterator.

Why is this bad?

Methods named iter or iter_mut conventionally return an Iterator.

Example

// `String` does not implement `Iterator`
struct Data {}
impl Data {
    fn iter(&self) -> String {
        todo!()
    }
}

Use instead:

use std::str::Chars;
struct Data {}
impl Data {
    fn iter(&self) -> Chars<'static> {
        todo!()
    }
}
Applicability: Unspecified(?)
Added in: 1.57.0

What it does

Checks for calls to iter, iter_mut or into_iter on empty collections

Why is this bad?

It is simpler to use the empty function from the standard library:

Example

use std::{slice, option};
let a: slice::Iter<i32> = [].iter();
let f: option::IntoIter<i32> = None.into_iter();

Use instead:

use std::iter;
let a: iter::Empty<i32> = iter::empty();
let b: iter::Empty<i32> = iter::empty();

Known problems

The type of the resulting iterator might become incompatible with its usage

Applicability: MaybeIncorrect(?)
Added in: 1.65.0

What it does

Checks for calls to iter, iter_mut or into_iter on collections containing a single item

Why is this bad?

It is simpler to use the once function from the standard library:

Example

let a = [123].iter();
let b = Some(123).into_iter();

Use instead:

use std::iter;
let a = iter::once(&123);
let b = iter::once(123);

Known problems

The type of the resulting iterator might become incompatible with its usage

Applicability: MaybeIncorrect(?)
Added in: 1.65.0

What it does

This is a restriction lint which prevents the use of hash types (i.e., HashSet and HashMap) in for loops.

Why restrict this?

Because hash types are unordered, when iterated through such as in a for loop, the values are returned in an undefined order. As a result, on redundant systems this may cause inconsistencies and anomalies. In addition, the unknown order of the elements may reduce readability or introduce other undesired side effects.

Example

    let my_map = std::collections::HashMap::<i32, String>::new();
    for (key, value) in my_map { /* ... */ }

Use instead:

    let my_map = std::collections::HashMap::<i32, String>::new();
    let mut keys = my_map.keys().clone().collect::<Vec<_>>();
    keys.sort();
    for key in keys {
        let value = &my_map[key];
    }
Applicability: Unspecified(?)
Added in: 1.76.0

What it does

Checks for usage of .drain(..) on Vec and VecDeque for iteration.

Why is this bad?

.into_iter() is simpler with better performance.

Example

let mut foo = vec![0, 1, 2, 3];
let bar: HashSet<usize> = foo.drain(..).collect();

Use instead:

let foo = vec![0, 1, 2, 3];
let bar: HashSet<usize> = foo.into_iter().collect();
Applicability: MaybeIncorrect(?)
Added in: 1.61.0

What it does

Looks for iter and iter_mut methods without an associated IntoIterator for (&|&mut) Type implementation.

Why is this bad?

It’s not bad, but having them is idiomatic and allows the type to be used in for loops directly (for val in &iter {}), without having to first call iter() or iter_mut().

Limitations

This lint focuses on providing an idiomatic API. Therefore, it will only lint on types which are accessible outside of the crate. For internal types, the IntoIterator trait can be implemented on demand if it is actually needed.

Example

struct MySlice<'a>(&'a [u8]);
impl<'a> MySlice<'a> {
    pub fn iter(&self) -> std::slice::Iter<'a, u8> {
        self.0.iter()
    }
}

Use instead:

struct MySlice<'a>(&'a [u8]);
impl<'a> MySlice<'a> {
    pub fn iter(&self) -> std::slice::Iter<'a, u8> {
        self.0.iter()
    }
}
impl<'a> IntoIterator for &MySlice<'a> {
    type Item = &'a u8;
    type IntoIter = std::slice::Iter<'a, u8>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}
Applicability: Unspecified(?)
Added in: 1.75.0

What it does

Warns if the digits of an integral or floating-point constant are grouped into groups that are too large.

Why is this bad?

Negatively impacts readability.

Example

let x: u64 = 6186491_8973511;
Applicability: MaybeIncorrect(?)
Added in: pre 1.29.0

What it does

It checks for the size of a Future created by async fn or async {}.

Why is this bad?

Due to the current unideal implementation of Coroutine, large size of a Future may cause stack overflows.

Example

async fn large_future(_x: [u8; 16 * 1024]) {}

pub async fn trigger() {
    large_future([0u8; 16 * 1024]).await;
}

Box::pin the big future instead.

async fn large_future(_x: [u8; 16 * 1024]) {}

pub async fn trigger() {
    Box::pin(large_future([0u8; 16 * 1024])).await;
}

Configuration

  • future-size-threshold: The maximum byte size a Future can have, before it triggers the clippy::large_futures lint

    (default: 16384)

Applicability: Unspecified(?)
Added in: 1.70.0

What it does

Checks for the inclusion of large files via include_bytes!() or include_str!().

Why restrict this?

Including large files can undesirably increase the size of the binary produced by the compiler. This lint may be used to catch mistakes where an unexpectedly large file is included, or temporarily to obtain a list of all large files.

Example

let included_str = include_str!("very_large_file.txt");
let included_bytes = include_bytes!("very_large_file.txt");

Use instead:

use std::fs;

// You can load the file at runtime
let string = fs::read_to_string("very_large_file.txt")?;
let bytes = fs::read("very_large_file.txt")?;

Configuration

  • max-include-file-size: The maximum size of a file included via include_bytes!() or include_str!(), in bytes

    (default: 1000000)

Applicability: Unspecified(?)
Added in: 1.62.0

What it does

Checks for local arrays that may be too large.

Why is this bad?

Large local arrays may cause stack overflow.

Example

let a = [0u32; 1_000_000];

Configuration

  • array-size-threshold: The maximum allowed size for arrays on the stack

    (default: 16384)

Applicability: Unspecified(?)
Added in: 1.41.0

What it does

Checks for functions that use a lot of stack space.

This often happens when constructing a large type, such as an array with a lot of elements, or constructing many smaller-but-still-large structs, or copying around a lot of large types.

This lint is a more general version of large_stack_arrays that is intended to look at functions as a whole instead of only individual array expressions inside of a function.

Why is this bad?

The stack region of memory is very limited in size (usually much smaller than the heap) and attempting to use too much will result in a stack overflow and crash the program. To avoid this, you should consider allocating large types on the heap instead (e.g. by boxing them).

Keep in mind that the code path to construction of large types does not even need to be reachable; it purely needs to exist inside of the function to contribute to the stack size. For example, this causes a stack overflow even though the branch is unreachable:

fn main() {
    if false {
        let x = [0u8; 10000000]; // 10 MB stack array
        black_box(&x);
    }
}

Known issues

False positives. The stack size that clippy sees is an estimated value and can be vastly different from the actual stack usage after optimizations passes have run (especially true in release mode). Modern compilers are very smart and are able to optimize away a lot of unnecessary stack allocations. In debug mode however, it is usually more accurate.

This lint works by summing up the size of all variables that the user typed, variables that were implicitly introduced by the compiler for temporaries, function arguments and the return value, and comparing them against a (configurable, but high-by-default).

Example

This function creates four 500 KB arrays on the stack. Quite big but just small enough to not trigger large_stack_arrays. However, looking at the function as a whole, it’s clear that this uses a lot of stack space.

struct QuiteLargeType([u8; 500_000]);
fn foo() {
    // ... some function that uses a lot of stack space ...
    let _x1 = QuiteLargeType([0; 500_000]);
    let _x2 = QuiteLargeType([0; 500_000]);
    let _x3 = QuiteLargeType([0; 500_000]);
    let _x4 = QuiteLargeType([0; 500_000]);
}

Instead of doing this, allocate the arrays on the heap. This currently requires going through a Vec first and then converting it to a Box:

struct NotSoLargeType(Box<[u8]>);

fn foo() {
    let _x1 = NotSoLargeType(vec![0; 500_000].into_boxed_slice());
//                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Now heap allocated.
//                                                                The size of `NotSoLargeType` is 16 bytes.
//  ...
}

Configuration

  • stack-size-threshold: The maximum allowed stack size for functions in bytes

    (default: 512000)

Applicability: Unspecified(?)
Added in: 1.72.0

What it does

Checks for functions taking arguments by value, where the argument type is Copy and large enough to be worth considering passing by reference. Does not trigger if the function is being exported, because that might induce API breakage, if the parameter is declared as mutable, or if the argument is a self.

Why is this bad?

Arguments passed by value might result in an unnecessary shallow copy, taking up more space in the stack and requiring a call to memcpy, which can be expensive.

Example

#[derive(Clone, Copy)]
struct TooLarge([u8; 2048]);

fn foo(v: TooLarge) {}

Use instead:

fn foo(v: &TooLarge) {}

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

  • pass-by-value-size-limit: The minimum size (in bytes) to consider a type for passing by reference instead of by value.

    (default: 256)

Applicability: MaybeIncorrect(?)
Added in: 1.49.0

What it does

Checks for let _ = <expr> where expr is #[must_use]

Why restrict this?

To ensure that all #[must_use] types are used rather than ignored.

Example

fn f() -> Result<u32, u32> {
    Ok(0)
}

let _ = f();
// is_ok() is marked #[must_use]
let _ = f().is_ok();
Applicability: Unspecified(?)
Added in: 1.42.0

What it does

Checks for let _ = <expr> without a type annotation, and suggests to either provide one, or remove the let keyword altogether.

Why restrict this?

The let _ = <expr> expression ignores the value of <expr>, but will continue to do so even if the type were to change, thus potentially introducing subtle bugs. By supplying a type annotation, one will be forced to re-visit the decision to ignore the value in such cases.

Known problems

The _ = <expr> is not properly supported by some tools (e.g. IntelliJ) and may seem odd to many developers. This lint also partially overlaps with the other let_underscore_* lints.

Example

fn foo() -> Result<u32, ()> {
    Ok(123)
}
let _ = foo();

Use instead:

fn foo() -> Result<u32, ()> {
    Ok(123)
}
// Either provide a type annotation:
let _: Result<u32, ()> = foo();
// …or drop the let keyword:
_ = foo();
Applicability: Unspecified(?)
Added in: 1.69.0

What it does

Checks for usage of any LinkedList, suggesting to use a Vec or a VecDeque (formerly called RingBuf).

Why is this bad?

Gankra says:

The TL;DR of LinkedList is that it’s built on a massive amount of pointers and indirection. It wastes memory, it has terrible cache locality, and is all-around slow. RingBuf, while “only” amortized for push/pop, should be faster in the general case for almost every possible workload, and isn’t even amortized at all if you can predict the capacity you need.

LinkedLists are only really good if you’re doing a lot of merging or splitting of lists. This is because they can just mangle some pointers instead of actually copying the data. Even if you’re doing a lot of insertion in the middle of the list, RingBuf can still be better because of how expensive it is to seek to the middle of a LinkedList.

Known problems

False positives – the instances where using a LinkedList makes sense are few and far between, but they can still happen.

Example

let x: LinkedList<usize> = LinkedList::new();

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks if string literals have formatting arguments outside of macros using them (like format!).

Why is this bad?

It will likely not generate the expected content.

Example

let x: Option<usize> = None;
let y = "hello";
x.expect("{y:?}");

Use instead:

let x: Option<usize> = None;
let y = "hello";
x.expect(&format!("{y:?}"));
Applicability: Unspecified(?)
Added in: 1.85.0

What it does

Checks for the usage of the to_le_bytes method and/or the function from_le_bytes.

Why restrict this?

To ensure use of big-endian or the target’s endianness rather than little-endian.

Example

let _x = 2i32.to_le_bytes();
let _y = 2i64.to_le_bytes();
Applicability: Unspecified(?)
Added in: 1.72.0

What it does

Checks for whole number float literals that cannot be represented as the underlying type without loss.

Why restrict this?

If the value was intended to be exact, it will not be. This may be especially surprising when the lost precision is to the left of the decimal point.

Example

let _: f32 = 16_777_217.0; // 16_777_216.0

Use instead:

let _: f32 = 16_777_216.0;
let _: f64 = 16_777_217.0;
Applicability: MachineApplicable(?)
Added in: 1.43.0

What it does

Checks for #[macro_use] use....

Why is this bad?

Since the Rust 2018 edition you can import macro’s directly, this is considered idiomatic.

Example

#[macro_use]
extern crate some_crate;

fn main() {
    some_macro!();
}

Use instead:

use some_crate::some_macro;

fn main() {
    some_macro!();
}
Applicability: MaybeIncorrect(?)
Added in: 1.44.0

What it does

Detects if-then-panic! that can be replaced with assert!.

Why is this bad?

assert! is simpler than if-then-panic!.

Example

let sad_people: Vec<&str> = vec![];
if !sad_people.is_empty() {
    panic!("there are sad people: {:?}", sad_people);
}

Use instead:

let sad_people: Vec<&str> = vec![];
assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
Applicability: MachineApplicable(?)
Added in: 1.57.0

What it does

Lints subtraction between Instant::now() and another Instant.

Why is this bad?

It is easy to accidentally write prev_instant - Instant::now(), which will always be 0ns as Instant subtraction saturates.

prev_instant.elapsed() also more clearly signals intention.

Example

use std::time::Instant;
let prev_instant = Instant::now();
let duration = Instant::now() - prev_instant;

Use instead:

use std::time::Instant;
let prev_instant = Instant::now();
let duration = prev_instant.elapsed();
Applicability: MachineApplicable(?)
Added in: 1.65.0

What it does

Checks for expressions like x.count_ones() == 1 or x & (x - 1) == 0, with x and unsigned integer, which may be manual reimplementations of x.is_power_of_two().

Why is this bad?

Manual reimplementations of is_power_of_two increase code complexity for little benefit.

Example

let a: u32 = 4;
let result = a.count_ones() == 1;

Use instead:

let a: u32 = 4;
let result = a.is_power_of_two();
Applicability: MachineApplicable(?)
Added in: 1.83.0

What it does

Checks for usage of option.map(f).unwrap_or_default() and result.map(f).unwrap_or_default() where f is a function or closure that returns the bool type.

Why is this bad?

Readability. These can be written more concisely as option.is_some_and(f) and result.is_ok_and(f).

Example

option.map(|a| a > 10).unwrap_or_default();
result.map(|a| a > 10).unwrap_or_default();

Use instead:

option.is_some_and(|a| a > 10);
result.is_ok_and(|a| a > 10);
Applicability: MachineApplicable(?)
Added in: 1.77.0

What it does

Warn of cases where let...else could be used

Why is this bad?

let...else provides a standard construct for this pattern that people can easily recognize. It’s also more compact.

Example

let v = if let Some(v) = w { v } else { return };

Could be written:

let Some(v) = w else { return };

Configuration

  • matches-for-let-else: Whether the matches should be considered by the lint, and whether there should be filtering for common types.

    (default: "WellKnownTypes")

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: HasPlaceholders(?)
Added in: 1.67.0

What it does

Checks for manual implementation of midpoint.

Why is this bad?

Using (x + y) / 2 might cause an overflow on the intermediate addition result.

Example

let c = (a + 10) / 2;

Use instead:

let c = u32::midpoint(a, 10);

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.87.0

What it does

Checks for usage of "" to create a String, such as "".to_string(), "".to_owned(), String::from("") and others.

Why is this bad?

Different ways of creating an empty string makes your code less standardized, which can be confusing.

Example

let a = "".to_string();
let b: String = "".into();

Use instead:

let a = String::new();
let b = String::new();
Applicability: MachineApplicable(?)
Added in: 1.65.0

What it does

Checks for too many variables whose name consists of a single character.

Why is this bad?

It’s hard to memorize what a variable means without a descriptive name.

Example

let (a, b, c, d, e, f, g) = (...);

Configuration

  • single-char-binding-names-threshold: The maximum number of single char bindings a scope may have

    (default: 4)

Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for instances of map_err(|_| Some::Enum)

Why restrict this?

This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error.

Example

Before:

use std::fmt;

#[derive(Debug)]
enum Error {
    Indivisible,
    Remainder(u8),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Indivisible => write!(f, "could not divide input by three"),
            Error::Remainder(remainder) => write!(
                f,
                "input is not divisible by three, remainder = {}",
                remainder
            ),
        }
    }
}

impl std::error::Error for Error {}

fn divisible_by_3(input: &str) -> Result<(), Error> {
    input
        .parse::<i32>()
        .map_err(|_| Error::Indivisible)
        .map(|v| v % 3)
        .and_then(|remainder| {
            if remainder == 0 {
                Ok(())
            } else {
                Err(Error::Remainder(remainder as u8))
            }
        })
}

After:

use std::{fmt, num::ParseIntError};

#[derive(Debug)]
enum Error {
    Indivisible(ParseIntError),
    Remainder(u8),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Indivisible(_) => write!(f, "could not divide input by three"),
            Error::Remainder(remainder) => write!(
                f,
                "input is not divisible by three, remainder = {}",
                remainder
            ),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::Indivisible(source) => Some(source),
            _ => None,
        }
    }
}

fn divisible_by_3(input: &str) -> Result<(), Error> {
    input
        .parse::<i32>()
        .map_err(Error::Indivisible)
        .map(|v| v % 3)
        .and_then(|remainder| {
            if remainder == 0 {
                Ok(())
            } else {
                Err(Error::Remainder(remainder as u8))
            }
        })
}
Applicability: Unspecified(?)
Added in: 1.48.0

What it does

Checks for usage of option.map(_).unwrap_or(_) or option.map(_).unwrap_or_else(_) or result.map(_).unwrap_or_else(_).

Why is this bad?

Readability, these can be written more concisely (resp.) as option.map_or(_, _), option.map_or_else(_, _) and result.map_or_else(_, _).

Known problems

The order of the arguments is not in execution order

Examples

option.map(|a| a + 1).unwrap_or(0);
option.map(|a| a > 10).unwrap_or(false);
result.map(|a| a + 1).unwrap_or_else(some_function);

Use instead:

option.map_or(0, |a| a + 1);
option.is_some_and(|a| a > 10);
result.map_or_else(some_function, |a| a + 1);

Past names

  • option_map_unwrap_or
  • option_map_unwrap_or_else
  • result_map_unwrap_or_else

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.45.0

What it does

Checks for Iterator::map over ranges without using the parameter which could be more clearly expressed using std::iter::repeat(...).take(...) or std::iter::repeat_n.

Why is this bad?

It expresses the intent more clearly to take the correct number of times from a generating function than to apply a closure to each number in a range only to discard them.

Example

let random_numbers : Vec<_> = (0..10).map(|_| { 3 + 1 }).collect();

Use instead:

let f : Vec<_> = std::iter::repeat( 3 + 1 ).take(10).collect();

Known Issues

This lint may suggest replacing a Map<Range> with a Take<RepeatWith>. The former implements some traits that the latter does not, such as DoubleEndedIterator.

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MaybeIncorrect(?)
Added in: 1.84.0

What it does

Checks for matches where match expression is a bool. It suggests to replace the expression with an if...else block.

Why is this bad?

It makes the code less readable.

Example

let condition: bool = true;
match condition {
    true => foo(),
    false => bar(),
}

Use if/else instead:

let condition: bool = true;
if condition {
    foo();
} else {
    bar();
}
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for match vec[idx] or match vec[n..m].

Why is this bad?

This can panic at runtime.

Example

let arr = vec![0, 1, 2, 3];
let idx = 1;

match arr[idx] {
    0 => println!("{}", 0),
    1 => println!("{}", 3),
    _ => {},
}

Use instead:

let arr = vec![0, 1, 2, 3];
let idx = 1;

match arr.get(idx) {
    Some(0) => println!("{}", 0),
    Some(1) => println!("{}", 3),
    _ => {},
}
Applicability: MaybeIncorrect(?)
Added in: 1.45.0

What it does

Checks for match with identical arm bodies.

Note: Does not lint on wildcards if the non_exhaustive_omitted_patterns_lint feature is enabled and disallowed.

Why is this bad?

This is probably a copy & paste error. If arm bodies are the same on purpose, you can factor them using |.

Example

match foo {
    Bar => bar(),
    Quz => quz(),
    Baz => bar(), // <= oops
}

This should probably be

match foo {
    Bar => bar(),
    Quz => quz(),
    Baz => baz(), // <= fixed
}

or if the original code was not a typo:

match foo {
    Bar | Baz => bar(), // <= shows the intent better
    Quz => quz(),
}
Applicability: MaybeIncorrect(?)
Added in: pre 1.29.0

What it does

Checks for arm which matches all errors with Err(_) and take drastic actions like panic!.

Why is this bad?

It is generally a bad practice, similar to catching all exceptions in java with catch(Exception)

Example

let x: Result<i32, &str> = Ok(3);
match x {
    Ok(_) => println!("ok"),
    Err(_) => panic!("err"),
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for wildcard enum matches for a single variant.

Why is this bad?

New enum variants added by library updates can be missed.

Known problems

Suggested replacements may not use correct path to enum if it’s not present in the current scope.

Example

match x {
    Foo::A => {},
    Foo::B => {},
    _ => {},
}

Use instead:

match x {
    Foo::A => {},
    Foo::B => {},
    Foo::C => {},
}
Applicability: MaybeIncorrect(?)
Added in: 1.45.0

What it does

Checks for iteration that may be infinite.

Why is this bad?

While there may be places where this is acceptable (e.g., in event streams), in most cases this is simply an error.

Known problems

The code may have a condition to stop iteration, but this lint is not clever enough to analyze it.

Example

let infinite_iter = 0..;
[0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of std::mem::forget(t) where t is Drop or has a field that implements Drop.

Why restrict this?

std::mem::forget(t) prevents t from running its destructor, possibly causing leaks. It is not possible to detect all means of creating leaks, but it may be desirable to prohibit the simple ones.

Example

mem::forget(Rc::new(55))
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for identifiers which consist of a single character (or fewer than the configured threshold).

Note: This lint can be very noisy when enabled; it may be desirable to only enable it temporarily.

Why restrict this?

To improve readability by requiring that every variable has a name more specific than a single letter can be.

Example

for m in movies {
    let title = m.t;
}

Use instead:

for movie in movies {
    let title = movie.title;
}

Configuration

  • allowed-idents-below-min-chars: Allowed names below the minimum allowed characters. The value ".." can be used as part of the list to indicate, that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value.

    (default: ["i", "j", "x", "y", "z", "w", "n"])

  • min-ident-chars-threshold: Minimum chars an ident can have, anything below or equal to this will be linted.

    (default: 1)

Applicability: Unspecified(?)
Added in: 1.72.0

What it does

Checks for type parameters which are positioned inconsistently between a type definition and impl block. Specifically, a parameter in an impl block which has the same name as a parameter in the type def, but is in a different place.

Why is this bad?

Type parameters are determined by their position rather than name. Naming type parameters inconsistently may cause you to refer to the wrong type parameter.

Limitations

This lint only applies to impl blocks with simple generic params, e.g. A. If there is anything more complicated, such as a tuple, it will be ignored.

Example

struct Foo<A, B> {
    x: A,
    y: B,
}
// inside the impl, B refers to Foo::A
impl<B, A> Foo<B, A> {}

Use instead:

struct Foo<A, B> {
    x: A,
    y: B,
}
impl<A, B> Foo<A, B> {}
Applicability: Unspecified(?)
Added in: 1.63.0

What it does

Checks assertions without a custom panic message.

Why restrict this?

Without a good custom message, it’d be hard to understand what went wrong when the assertion fails. A good custom message should be more about why the failure of the assertion is problematic and not what is failed because the assertion already conveys that.

Although the same reasoning applies to testing functions, this lint ignores them as they would be too noisy. Also, in most cases understanding the test failure would be easier compared to understanding a complex invariant distributed around the codebase.

Known problems

This lint cannot check the quality of the custom panic messages. Hence, you can suppress this lint simply by adding placeholder messages like “assertion failed”. However, we recommend coming up with good messages that provide useful information instead of placeholder messages that don’t provide any extra information.

Example

fn call(service: Service) {
    assert!(service.ready);
}

Use instead:

fn call(service: Service) {
    assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests");
}
Applicability: Unspecified(?)
Added in: 1.70.0

What it does

Checks for repeated slice indexing without asserting beforehand that the length is greater than the largest index used to index into the slice.

Why restrict this?

In the general case where the compiler does not have a lot of information about the length of a slice, indexing it repeatedly will generate a bounds check for every single index.

Asserting that the length of the slice is at least as large as the largest value to index beforehand gives the compiler enough information to elide the bounds checks, effectively reducing the number of bounds checks from however many times the slice was indexed to just one (the assert).

Drawbacks

False positives. It is, in general, very difficult to predict how well the optimizer will be able to elide bounds checks and it very much depends on the surrounding code. For example, indexing into the slice yielded by the slice::chunks_exact iterator will likely have all of the bounds checks elided even without an assert if the chunk_size is a constant.

Asserts are not tracked across function calls. Asserting the length of a slice in a different function likely gives the optimizer enough information about the length of a slice, but this lint will not detect that.

Example

fn sum(v: &[u8]) -> u8 {
    // 4 bounds checks
    v[0] + v[1] + v[2] + v[3]
}

Use instead:

fn sum(v: &[u8]) -> u8 {
    assert!(v.len() > 3);
    // no bounds checks
    v[0] + v[1] + v[2] + v[3]
}
Applicability: MachineApplicable(?)
Added in: 1.74.0

What it does

Suggests the use of const in functions and methods where possible.

Why is this bad?

Not having the function const prevents callers of the function from being const as well.

Known problems

Const functions are currently still being worked on, with some features only being available on nightly. This lint does not consider all edge cases currently and the suggestions may be incorrect if you are using this lint on stable.

Also, the lint only runs one pass over the code. Consider these two non-const functions:

fn a() -> i32 {
    0
}
fn b() -> i32 {
    a()
}

When running Clippy, the lint will only suggest to make a const, because b at this time can’t be const as it calls a non-const function. Making a const and running Clippy again, will suggest to make b const, too.

If you are marking a public function with const, removing it again will break API compatibility.

Example

fn new() -> Self {
    Self { random_number: 42 }
}

Could be a const fn:

const fn new() -> Self {
    Self { random_number: 42 }
}

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.34.0

What it does

Warns if there is missing documentation for any private documentable item.

Why restrict this?

Doc is good. rustc has a MISSING_DOCS allowed-by-default lint for public members, but has no way to enforce documentation of private items. This lint fixes that.

Configuration

  • missing-docs-in-crate-items: Whether to only check for missing documentation in items visible within the current crate. For example, pub(crate) items.

    (default: false)

Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks the doc comments of publicly visible functions that return a Result type and warns if there is no # Errors section.

Why is this bad?

Documenting the type of errors that can be returned from a function can help callers write code to handle the errors appropriately.

Examples

Since the following function returns a Result it has an # Errors section in its doc comment:

/// # Errors
///
/// Will return `Err` if `filename` does not exist or the user does not have
/// permission to read it.
pub fn read(filename: String) -> io::Result<String> {
    unimplemented!();
}

Configuration

  • check-private-items: Whether to also run the listed lints on private items.

    (default: false)

Applicability: Unspecified(?)
Added in: 1.41.0

What it does

Checks for manual core::fmt::Debug implementations that do not use all fields.

Why is this bad?

A common mistake is to forget to update manual Debug implementations when adding a new field to a struct or a new variant to an enum.

At the same time, it also acts as a style lint to suggest using core::fmt::DebugStruct::finish_non_exhaustive for the times when the user intentionally wants to leave out certain fields (e.g. to hide implementation details).

Known problems

This lint works based on the DebugStruct helper types provided by the Formatter, so this won’t detect Debug impls that use the write! macro. Oftentimes there is more logic to a Debug impl if it uses write! macro, so it tries to be on the conservative side and not lint in those cases in an attempt to prevent false positives.

This lint also does not look through function calls, so calling a function does not consider fields used inside of that function as used by the Debug impl.

Lastly, it also ignores tuple structs as their DebugTuple formatter does not have a finish_non_exhaustive method, as well as enums because their exhaustiveness is already checked by the compiler when matching on the enum, making it much less likely to accidentally forget to update the Debug impl when adding a new variant.

Example

use std::fmt;
struct Foo {
    data: String,
    // implementation detail
    hidden_data: i32
}
impl fmt::Debug for Foo {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("Foo")
            .field("data", &self.data)
            .finish()
    }
}

Use instead:

use std::fmt;
struct Foo {
    data: String,
    // implementation detail
    hidden_data: i32
}
impl fmt::Debug for Foo {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("Foo")
            .field("data", &self.data)
            .finish_non_exhaustive()
    }
}
Applicability: Unspecified(?)
Added in: 1.70.0

What it does

It lints if an exported function, method, trait method with default impl, or trait method impl is not #[inline].

Why restrict this?

When a function is not marked #[inline], it is not a “small” candidate for automatic inlining, and LTO is not in use, then it is not possible for the function to be inlined into the code of any crate other than the one in which it is defined. Depending on the role of the function and the relationship of the crates, this could significantly reduce performance.

Certain types of crates might intend for most of the methods in their public API to be able to be inlined across crates even when LTO is disabled. This lint allows those crates to require all exported methods to be #[inline] by default, and then opt out for specific methods where this might not make sense.

Example

pub fn foo() {} // missing #[inline]
fn ok() {} // ok
#[inline] pub fn bar() {} // ok
#[inline(always)] pub fn baz() {} // ok

pub trait Bar {
  fn bar(); // ok
  fn def_bar() {} // missing #[inline]
}

struct Baz;
impl Baz {
    fn private() {} // ok
}

impl Bar for Baz {
  fn bar() {} // ok - Baz is not exported
}

pub struct PubBaz;
impl PubBaz {
    fn private() {} // ok
    pub fn not_private() {} // missing #[inline]
}

impl Bar for PubBaz {
    fn bar() {} // missing #[inline]
    fn def_bar() {} // missing #[inline]
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks the doc comments of publicly visible functions that may panic and warns if there is no # Panics section.

Why is this bad?

Documenting the scenarios in which panicking occurs can help callers who do not want to panic to avoid those situations.

Examples

Since the following function may panic it has a # Panics section in its doc comment:

/// # Panics
///
/// Will panic if y is 0
pub fn divide_by(x: i32, y: i32) -> i32 {
    if y == 0 {
        panic!("Cannot divide by 0")
    } else {
        x / y
    }
}

Individual panics within a function can be ignored with #[expect] or #[allow]:

pub fn will_not_panic(x: usize) {
    #[expect(clippy::missing_panics_doc, reason = "infallible")]
    let y = NonZeroUsize::new(1).unwrap();

    // If any panics are added in the future the lint will still catch them
}

Configuration

  • check-private-items: Whether to also run the listed lints on private items.

    (default: false)

Applicability: Unspecified(?)
Added in: 1.51.0

What it does

Checks if a provided method is used implicitly by a trait implementation.

Why restrict this?

To ensure that a certain implementation implements every method; for example, a wrapper type where every method should delegate to the corresponding method of the inner type’s implementation.

This lint should typically be enabled on a specific trait impl item rather than globally.

Example

trait Trait {
    fn required();

    fn provided() {}
}

#[warn(clippy::missing_trait_methods)]
impl Trait for Type {
    fn required() { /* ... */ }
}

Use instead:

trait Trait {
    fn required();

    fn provided() {}
}

#[warn(clippy::missing_trait_methods)]
impl Trait for Type {
    fn required() { /* ... */ }

    fn provided() { /* ... */ }
}
Applicability: Unspecified(?)
Added in: 1.66.0

What it does

Checks for a read and a write to the same variable where whether the read occurs before or after the write depends on the evaluation order of sub-expressions.

Why restrict this?

While [the evaluation order of sub-expressions] is fully specified in Rust, it still may be confusing to read an expression where the evaluation order affects its behavior.

Known problems

Code which intentionally depends on the evaluation order, or which is correct for any evaluation order.

Example

let mut x = 0;

let a = {
    x = 1;
    1
} + x;
// Unclear whether a is 1 or 2.

Use instead:

let tmp = {
    x = 1;
    1
};
let a = tmp + x;

Past names

  • eval_order_dependence
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks that module layout uses only self named module files; bans mod.rs files.

Why restrict this?

Having multiple module layout styles in a project can be confusing.

Example

src/
  stuff/
    stuff_files.rs
    mod.rs
  lib.rs

Use instead:

src/
  stuff/
    stuff_files.rs
  stuff.rs
  lib.rs
Applicability: Unspecified(?)
Added in: 1.57.0

What it does

Detects public item names that are prefixed or suffixed by the containing public module’s name.

Why is this bad?

It requires the user to type the module name twice in each usage, especially if they choose to import the module rather than its contents.

Lack of such repetition is also the style used in the Rust standard library; e.g. io::Error and fmt::Error rather than io::IoError and fmt::FmtError; and array::from_ref rather than array::array_from_ref.

Known issues

Glob re-exports are ignored; e.g. this will not warn even though it should:

pub mod foo {
    mod iteration {
        pub struct FooIter {}
    }
    pub use iteration::*; // creates the path `foo::FooIter`
}

Example

mod cake {
    struct BlackForestCake;
}

Use instead:

mod cake {
    struct BlackForest;
}

Past names

  • stutter

Configuration

  • allowed-prefixes: List of prefixes to allow when determining whether an item’s name ends with the module’s name. If the rest of an item’s name is an allowed prefix (e.g. item ToFoo or to_foo in module foo), then don’t emit a warning.

Example

allowed-prefixes = [ "to", "from" ]

Noteworthy

  • By default, the following prefixes are allowed: to, as, into, from, try_into and try_from

  • PascalCase variant is included automatically for each snake_case variant (e.g. if try_into is included, TryInto will also be included)

  • Use ".." as part of the list to indicate that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value

    (default: ["to", "as", "into", "from", "try_into", "try_from"])

Applicability: Unspecified(?)
Added in: 1.33.0

What it does

Checks for modulo arithmetic.

Why restrict this?

The results of modulo (%) operation might differ depending on the language, when negative numbers are involved. If you interop with different languages it might be beneficial to double check all places that use modulo arithmetic.

For example, in Rust 17 % -3 = 2, but in Python 17 % -3 = -1.

Example

let x = -17 % 3;

Configuration

  • allow-comparison-to-zero: Don’t lint when comparing the result of a modulo operation to zero.

    (default: true)

Applicability: Unspecified(?)
Added in: 1.42.0

What it does

Checks to see if multiple versions of a crate are being used.

Why is this bad?

This bloats the size of targets, and can lead to confusing error messages when structs or traits are used interchangeably between different versions of a crate.

Known problems

Because this can be caused purely by the dependencies themselves, it’s not always possible to fix this issue. In those cases, you can allow that specific crate using the allowed_duplicate_crates configuration option.

Example

[dependencies]
ctrlc = "=3.1.0"
ansi_term = "=0.11.0"

Configuration

  • allowed-duplicate-crates: A list of crate names to allow duplicates of

    (default: [])

Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for multiple inherent implementations of a struct

Why restrict this?

Splitting the implementation of a type makes the code harder to navigate.

Example

struct X;
impl X {
    fn one() {}
}
impl X {
    fn other() {}
}

Could be written:

struct X;
impl X {
    fn one() {}
    fn other() {}
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for unsafe blocks that contain more than one unsafe operation.

Why restrict this?

Combined with undocumented_unsafe_blocks, this lint ensures that each unsafe operation must be independently justified. Combined with unused_unsafe, this lint also ensures elimination of unnecessary unsafe blocks through refactoring.

Example

/// Reads a `char` from the given pointer.
///
/// # Safety
///
/// `ptr` must point to four consecutive, initialized bytes which
/// form a valid `char` when interpreted in the native byte order.
fn read_char(ptr: *const u8) -> char {
    // SAFETY: The caller has guaranteed that the value pointed
    // to by `bytes` is a valid `char`.
    unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
}

Use instead:

/// Reads a `char` from the given pointer.
///
/// # Safety
///
/// - `ptr` must be 4-byte aligned, point to four consecutive
///   initialized bytes, and be valid for reads of 4 bytes.
/// - The bytes pointed to by `ptr` must represent a valid
///   `char` when interpreted in the native byte order.
fn read_char(ptr: *const u8) -> char {
    // SAFETY: `ptr` is 4-byte aligned, points to four consecutive
    // initialized bytes, and is valid for reads of 4 bytes.
    let int_value = unsafe { *ptr.cast::<u32>() };

    // SAFETY: The caller has guaranteed that the four bytes
    // pointed to by `bytes` represent a valid `char`.
    unsafe { char::from_u32_unchecked(int_value) }
}
Applicability: Unspecified(?)
Added in: 1.69.0

What it does

Checks for public functions that have no #[must_use] attribute, but return something not already marked must-use, have no mutable arg and mutate no statics.

Why is this bad?

Not bad at all, this lint just shows places where you could add the attribute.

Known problems

The lint only checks the arguments for mutable types without looking if they are actually changed. On the other hand, it also ignores a broad range of potentially interesting side effects, because we cannot decide whether the programmer intends the function to be called for the side effect or the result. Expect many false positives. At least we don’t lint if the result type is unit or already #[must_use].

Examples

// this could be annotated with `#[must_use]`.
pub fn id<T>(t: T) -> T { t }
Applicability: MachineApplicable(?)
Added in: 1.40.0

What it does

Checks for instances of mut mut references.

Why is this bad?

Multiple muts don’t add anything meaningful to the source. This is either a copy’n’paste error, or it shows a fundamental misunderstanding of references.

Example

let x = &mut &mut y;
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of Mutex<X> where an atomic will do.

Why restrict this?

Using a mutex just to make access to a plain bool or reference sequential is shooting flies with cannons. std::sync::atomic::AtomicBool and std::sync::atomic::AtomicPtr are leaner and faster.

On the other hand, Mutexes are, in general, easier to verify correctness. An atomic does not behave the same as an equivalent mutex. See this issue’s commentary for more details.

Known problems

  • This lint cannot detect if the mutex is actually used for waiting before a critical section.
  • This lint has a false positive that warns without considering the case where Mutex is used together with Condvar.

Example

let x = Mutex::new(&y);

Use instead:

let x = AtomicBool::new(y);
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of Mutex<X> where X is an integral type.

Why restrict this?

Using a mutex just to make access to a plain integer sequential is shooting flies with cannons. std::sync::atomic::AtomicUsize is leaner and faster.

On the other hand, Mutexes are, in general, easier to verify correctness. An atomic does not behave the same as an equivalent mutex. See this issue’s commentary for more details.

Known problems

  • This lint cannot detect if the mutex is actually used for waiting before a critical section.
  • This lint has a false positive that warns without considering the case where Mutex is used together with Condvar.
  • This lint suggest using AtomicU64 instead of Mutex<u64>, but AtomicU64 is not available on some 32-bit platforms.

Example

let x = Mutex::new(0usize);

Use instead:

let x = AtomicUsize::new(0usize);
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for naive byte counts

Why is this bad?

The bytecount crate has methods to count your bytes faster, especially for large slices.

Known problems

If you have predominantly small slices, the bytecount::count(..) method may actually be slower. However, if you can ensure that less than 2³²-1 matches arise, the naive_count_32(..) can be faster in those cases.

Example

let count = vec.iter().filter(|x| **x == 0u8).count();

Use instead:

let count = bytecount::count(&vec, 0u8);
Applicability: MaybeIncorrect(?)
Added in: pre 1.29.0

What it does

Checks for usage of bitwise and/or operators between booleans, where performance may be improved by using a lazy and.

Why is this bad?

The bitwise operators do not support short-circuiting, so it may hinder code performance. Additionally, boolean logic “masked” as bitwise logic is not caught by lints like unnecessary_fold

Known problems

This lint evaluates only when the right side is determined to have no side effects. At this time, that determination is quite conservative.

Example

let (x,y) = (true, false);
if x & !y {} // where both x and y are booleans

Use instead:

let (x,y) = (true, false);
if x && !y {}
Applicability: MachineApplicable(?)
Added in: 1.54.0

What it does

Checks for functions collecting an iterator when collect is not needed.

Why is this bad?

collect causes the allocation of a new data structure, when this allocation may not be needed.

Example

let len = iterator.collect::<Vec<_>>().len();

Use instead:

let len = iterator.count();
Applicability: MachineApplicable(?)
Added in: 1.30.0

What it does

The lint checks for if-statements appearing in loops that contain a continue statement in either their main blocks or their else-blocks, when omitting the else-block possibly with some rearrangement of code can make the code easier to understand. The lint also checks if the last statement in the loop is a continue

Why is this bad?

Having explicit else blocks for if statements containing continue in their THEN branch adds unnecessary branching and nesting to the code. Having an else block containing just continue can also be better written by grouping the statements following the whole if statement within the THEN block and omitting the else block completely.

Example

while condition() {
    update_condition();
    if x {
        // ...
    } else {
        continue;
    }
    println!("Hello, world");
}

Could be rewritten as

while condition() {
    update_condition();
    if x {
        // ...
        println!("Hello, world");
    }
}

As another example, the following code

loop {
    if waiting() {
        continue;
    } else {
        // Do something useful
    }
    # break;
}

Could be rewritten as

loop {
    if waiting() {
        continue;
    }
    // Do something useful
    # break;
}

fn foo() -> ErrorKind { ErrorKind::NotFound }
for _ in 0..10 {
    match foo() {
        ErrorKind::NotFound => {
            eprintln!("not found");
            continue
        }
        ErrorKind::TimedOut => {
            eprintln!("timeout");
            continue
        }
        _ => {
            eprintln!("other error");
            continue
        }
    }
}

Could be rewritten as


fn foo() -> ErrorKind { ErrorKind::NotFound }
for _ in 0..10 {
    match foo() {
        ErrorKind::NotFound => {
            eprintln!("not found");
        }
        ErrorKind::TimedOut => {
            eprintln!("timeout");
        }
        _ => {
            eprintln!("other error");
        }
    }
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of for_each that would be more simply written as a for loop.

Why is this bad?

for_each may be used after applying iterator transformers like filter for better readability and performance. It may also be used to fit a simple operation on one line. But when none of these apply, a simple for loop is more idiomatic.

Example

let v = vec![0, 1, 2];
v.iter().for_each(|elem| {
    println!("{elem}");
})

Use instead:

let v = vec![0, 1, 2];
for elem in &v {
    println!("{elem}");
}

Known Problems

When doing things such as:

let v = vec![0, 1, 2];
v.iter().for_each(|elem| unsafe {
    libc::printf(c"%d\n".as_ptr(), elem);
});

This lint will not trigger.

Applicability: MachineApplicable(?)
Added in: 1.53.0

What it does

Check if a &mut function argument is actually used mutably.

Be careful if the function is publicly reexported as it would break compatibility with users of this function, when the users pass this function as an argument.

Why is this bad?

Less mut means less fights with the borrow checker. It can also lead to more opportunities for parallelization.

Example

fn foo(y: &mut i32) -> i32 {
    12 + *y
}

Use instead:

fn foo(y: &i32) -> i32 {
    12 + *y
}

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

Applicability: Unspecified(?)
Added in: 1.73.0

What it does

Checks for functions taking arguments by value, but not consuming them in its body.

Why is this bad?

Taking arguments by reference is more flexible and can sometimes avoid unnecessary allocations.

Known problems

  • This lint suggests taking an argument by reference, however sometimes it is better to let users decide the argument type (by using Borrow trait, for example), depending on how the function is used.

Example

fn foo(v: Vec<i32>) {
    assert_eq!(v.len(), 42);
}

should be

fn foo(v: &[i32]) {
    assert_eq!(v.len(), 42);
}
Applicability: MaybeIncorrect(?)
Added in: pre 1.29.0

What it does

Checks for raw string literals with an unnecessary amount of hashes around them.

Why is this bad?

It’s just unnecessary, and makes it look like there’s more escaping needed than is actually necessary.

Example

let r = r###"Hello, "world"!"###;

Use instead:

let r = r#"Hello, "world"!"#;

Configuration

  • allow-one-hash-in-raw-strings: Whether to allow r#""# when r"" can be used

    (default: false)

Applicability: MachineApplicable(?)
Added in: 1.72.0

What it does

Checks for raw string literals where a string literal can be used instead.

Why restrict this?

For consistent style by using simpler string literals whenever possible.

However, there are many cases where using a raw string literal is more idiomatic than a string literal, so it’s opt-in.

Example

let r = r"Hello, world!";

Use instead:

let r = "Hello, world!";
Applicability: MachineApplicable(?)
Added in: 1.72.0

What it does

Checks for negative feature names with prefix no- or not-

Why is this bad?

Features are supposed to be additive, and negatively-named features violate it.

Example

[features]
default = []
no-abc = []
not-def = []

Use instead:

[features]
default = ["abc", "def"]
abc = []
def = []

Applicability: Unspecified(?)
Added in: 1.57.0

What it does

Checks for binding to underscore prefixed variable without side-effects.

Why is this bad?

Unlike dead code, these bindings are actually executed. However, as they have no effect and shouldn’t be used further on, all they do is make the code less readable.

Example

let _i_serve_no_purpose = 1;
Applicability: Unspecified(?)
Added in: 1.58.0

What it does

Checks for Rust ABI functions with the #[no_mangle] attribute.

Why is this bad?

The Rust ABI is not stable, but in many simple cases matches enough with the C ABI that it is possible to forget to add extern "C" to a function called from C. Changes to the Rust ABI can break this at any point.

Example

 #[no_mangle]
 fn example(arg_one: u32, arg_two: usize) {}

Use instead:

 #[no_mangle]
 extern "C" fn example(arg_one: u32, arg_two: usize) {}
Applicability: MaybeIncorrect(?)
Added in: 1.69.0

What it does

Checks for non-ASCII characters in string and char literals.

Why restrict this?

Yeah, we know, the 90’s called and wanted their charset back. Even so, there still are editors and other programs out there that don’t work well with Unicode. So if the code is meant to be used internationally, on multiple operating systems, or has other portability requirements, activating this lint could be useful.

Example

let x = String::from("€");

Use instead:

let x = String::from("\u{20ac}");
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

This lint warns about a Send implementation for a type that contains fields that are not safe to be sent across threads. It tries to detect fields that can cause a soundness issue when sent to another thread (e.g., Rc) while allowing !Send fields that are expected to exist in a Send type, such as raw pointers.

Why is this bad?

Sending the struct to another thread effectively sends all of its fields, and the fields that do not implement Send can lead to soundness bugs such as data races when accessed in a thread that is different from the thread that created it.

See:

Known Problems

This lint relies on heuristics to distinguish types that are actually unsafe to be sent across threads and !Send types that are expected to exist in Send type. Its rule can filter out basic cases such as Vec<*const T>, but it’s not perfect. Feel free to create an issue if you have a suggestion on how this heuristic can be improved.

Example

struct ExampleStruct<T> {
    rc_is_not_send: Rc<String>,
    unbounded_generic_field: T,
}

// This impl is unsound because it allows sending `!Send` types through `ExampleStruct`
unsafe impl<T> Send for ExampleStruct<T> {}

Use thread-safe types like std::sync::Arc or specify correct bounds on generic type parameters (T: Send).

Configuration

  • enable-raw-pointer-heuristic-for-send: Whether to apply the raw pointer heuristic to determine if a type is Send.

    (default: true)

Applicability: Unspecified(?)
Added in: 1.57.0

What it does

Lints when once_cell::sync::Lazy or lazy_static! are used to define a static variable, and suggests replacing such cases with std::sync::LazyLock instead.

Note: This lint will not trigger in crate with no_std context, or with MSRV < 1.80.0. It also will not trigger on once_cell::sync::Lazy usage in crates which use other types from once_cell, such as once_cell::race::OnceBox.

Why restrict this?

  • Reduces the need for an extra dependency
  • Enforce convention of using standard library types when possible

Example

lazy_static! {
    static ref FOO: String = "foo".to_uppercase();
}
static BAR: once_cell::sync::Lazy<String> = once_cell::sync::Lazy::new(|| "BAR".to_lowercase());

Use instead:

static FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "FOO".to_lowercase());
static BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "BAR".to_lowercase());

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.81.0

What it does

Checks for conversions from NonZero types to regular integer types, and suggests using NonZero types for the target as well.

Why is this bad?

Converting from NonZero types to regular integer types and then back to NonZero types is less efficient and loses the type-safety guarantees provided by NonZero types. Using NonZero types consistently can lead to more optimized code and prevent certain classes of errors related to zero values.

Example

use std::num::{NonZeroU32, NonZeroU64};

fn example(x: u64, y: NonZeroU32) {
    // Bad: Converting NonZeroU32 to u64 unnecessarily
    let r1 = x / u64::from(y.get());
    let r2 = x % u64::from(y.get());
}

Use instead:

use std::num::{NonZeroU32, NonZeroU64};

fn example(x: u64, y: NonZeroU32) {
    // Good: Preserving the NonZero property
    let r1 = x / NonZeroU64::from(y);
    let r2 = x % NonZeroU64::from(y);
}
Applicability: MachineApplicable(?)
Added in: 1.83.0

What it does

Checks that common macros are used with consistent bracing.

Why is this bad?

This is mostly a consistency lint although using () or [] doesn’t give you a semicolon in item position, which can be unexpected.

Example

vec!{1, 2, 3};

Use instead:

vec![1, 2, 3];

Configuration

  • standard-macro-braces: Enforce the named macros always use the braces specified.

A MacroMatcher can be added like so { name = "macro_name", brace = "(" }. If the macro could be used with a full path two MacroMatchers have to be added one with the full path crate_name::macro_name and one with just the macro name.

(default: [])

Applicability: MachineApplicable(?)
Added in: 1.55.0

What it does

Checks for usage of .as_ref().cloned() and .as_mut().cloned() on Options

Why is this bad?

This can be written more concisely by cloning the Option directly.

Example

fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> {
    bar.as_ref().cloned()
}

Use instead:

fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> {
    bar.clone()
}
Applicability: MachineApplicable(?)
Added in: 1.77.0

What it does

Lints usage of if let Some(v) = ... { y } else { x } and match .. { Some(v) => y, None/_ => x } which are more idiomatically done with Option::map_or (if the else bit is a pure expression) or Option::map_or_else (if the else bit is an impure expression).

Why is this bad?

Using the dedicated functions of the Option type is clearer and more concise than an if let expression.

Notes

This lint uses a deliberately conservative metric for checking if the inside of either body contains loop control expressions break or continue (which cannot be used within closures). If these are found, this lint will not be raised.

Example

let _ = if let Some(foo) = optional {
    foo
} else {
    5
};
let _ = match optional {
    Some(val) => val + 1,
    None => 5
};
let _ = if let Some(foo) = optional {
    foo
} else {
    let y = do_complicated_function();
    y*y
};

should be

let _ = optional.map_or(5, |foo| foo);
let _ = optional.map_or(5, |val| val + 1);
let _ = optional.map_or_else(||{
    let y = do_complicated_function();
    y*y
}, |foo| foo);
Applicability: MaybeIncorrect(?)
Added in: 1.47.0

What it does

Checks for usage of Option<Option<_>> in function signatures and type definitions

Why is this bad?

Option<_> represents an optional value. Option<Option<_>> represents an optional value which itself wraps an optional. This is logically the same thing as an optional value but has an unneeded extra level of wrapping.

If you have a case where Some(Some(_)), Some(None) and None are distinct cases, consider a custom enum instead, with clear names for each case.

Example

fn get_data() -> Option<Option<u32>> {
    None
}

Better:

pub enum Contents {
    Data(Vec<u8>), // Was Some(Some(Vec<u8>))
    NotYetFetched, // Was Some(None)
    None,          // Was None
}

fn get_data() -> Contents {
    Contents::None
}

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for calls to .or(foo(..)), .unwrap_or(foo(..)), .or_insert(foo(..)) etc., and suggests to use .or_else(|| foo(..)), .unwrap_or_else(|| foo(..)), .unwrap_or_default() or .or_default() etc. instead.

Why is this bad?

The function will always be called. This is only bad if it allocates or does some non-trivial amount of work.

Known problems

If the function has side-effects, not calling it will change the semantic of the program, but you shouldn’t rely on that.

The lint also cannot figure out whether the function you call is actually expensive to call or not.

Example

foo.unwrap_or(String::from("empty"));

Use instead:

foo.unwrap_or_else(|| String::from("empty"));
Applicability: HasPlaceholders(?)
Added in: pre 1.29.0

What it does

Checks for usage of panic!.

Why restrict this?

This macro, or panics in general, may be unwanted in production code.

Example

panic!("even with a good reason");

Configuration

  • allow-panic-in-tests: Whether panic should be allowed in test functions or #[cfg(test)]

    (default: false)

Applicability: Unspecified(?)
Added in: 1.40.0

What it does

Checks for usage of panic! or assertions in a function whose return type is Result.

Why restrict this?

For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.

Known problems

Functions called from a function returning a Result may invoke a panicking macro. This is not checked.

Example

fn result_with_panic() -> Result<bool, String>
{
    panic!("error");
}

Use instead:

fn result_without_panic() -> Result<bool, String> {
    Err(String::from("error"))
}
Applicability: Unspecified(?)
Added in: 1.48.0

What it does

Checks whether some but not all fields of a struct are public.

Either make all fields of a type public, or make none of them public

Why restrict this?

Most types should either be:

  • Abstract data types: complex objects with opaque implementation which guard interior invariants and expose intentionally limited API to the outside world.
  • Data: relatively simple objects which group a bunch of related attributes together, but have no invariants.

Example

pub struct Color {
    pub r: u8,
    pub g: u8,
    b: u8,
}

Use instead:

pub struct Color {
    pub r: u8,
    pub g: u8,
    pub b: u8,
}
Applicability: Unspecified(?)
Added in: 1.66.0

What it does

  • Checks for push calls on PathBuf that can cause overwrites.

Why is this bad?

Calling push with a root path at the start can overwrite the previous defined path.

Example

use std::path::PathBuf;

let mut x = PathBuf::from("/foo");
x.push("/bar");
assert_eq!(x, PathBuf::from("/bar"));

Could be written:

use std::path::PathBuf;

let mut x = PathBuf::from("/foo");
x.push("bar");
assert_eq!(x, PathBuf::from("/foo/bar"));
Applicability: MaybeIncorrect(?)
Added in: 1.36.0

What it does

Checks for calls to push immediately after creating a new PathBuf.

Why is this bad?

Multiple .join() calls are usually easier to read than multiple .push calls across multiple statements. It might also be possible to use PathBuf::from instead.

Known problems

.join() introduces an implicit clone(). PathBuf::from can alternatively be used when the PathBuf is newly constructed. This will avoid the implicit clone.

Example

let mut path_buf = PathBuf::new();
path_buf.push("foo");

Use instead:

let path_buf = PathBuf::from("foo");
// or
let path_buf = PathBuf::new().join("foo");
Applicability: HasPlaceholders(?)
Added in: 1.82.0

What it does

Checks for patterns that aren’t exact representations of the types they are applied to.

To satisfy this lint, you will have to adjust either the expression that is matched against or the pattern itself, as well as the bindings that are introduced by the adjusted patterns. For matching you will have to either dereference the expression with the * operator, or amend the patterns to explicitly match against &<pattern> or &mut <pattern> depending on the reference mutability. For the bindings you need to use the inverse. You can leave them as plain bindings if you wish for the value to be copied, but you must use ref mut <variable> or ref <variable> to construct a reference into the matched structure.

If you are looking for a way to learn about ownership semantics in more detail, it is recommended to look at IDE options available to you to highlight types, lifetimes and reference semantics in your code. The available tooling would expose these things in a general way even outside of the various pattern matching mechanics. Of course this lint can still be used to highlight areas of interest and ensure a good understanding of ownership semantics.

Why restrict this?

It increases ownership hints in the code, and will guard against some changes in ownership.

Example

This example shows the basic adjustments necessary to satisfy the lint. Note how the matched expression is explicitly dereferenced with * and the inner variable is bound to a shared borrow via ref inner.

// Bad
let value = &Some(Box::new(23));
match value {
    Some(inner) => println!("{}", inner),
    None => println!("none"),
}

// Good
let value = &Some(Box::new(23));
match *value {
    Some(ref inner) => println!("{}", inner),
    None => println!("none"),
}

The following example demonstrates one of the advantages of the more verbose style. Note how the second version uses ref mut a to explicitly declare a a shared mutable borrow, while b is simply taken by value. This ensures that the loop body cannot accidentally modify the wrong part of the structure.

// Bad
let mut values = vec![(2, 3), (3, 4)];
for (a, b) in &mut values {
    *a += *b;
}

// Good
let mut values = vec![(2, 3), (3, 4)];
for &mut (ref mut a, b) in &mut values {
    *a += b;
}
Applicability: Unspecified(?)
Added in: 1.47.0

What it does

Checks for bit shifting operations combined with bit masking/combining operators and suggest using parentheses.

Why restrict this?

Not everyone knows the precedence of those operators by heart, so expressions like these may trip others trying to reason about the code.

Example

0x2345 & 0xF000 >> 12 equals 5, while (0x2345 & 0xF000) >> 12 equals 2

Applicability: MachineApplicable(?)
Added in: 1.86.0

What it does

Checks for as casts between raw pointers that don’t change their constness, namely *const T to *const U and *mut T to *mut U.

Why is this bad?

Though as casts between raw pointers are not terrible, pointer::cast is safer because it cannot accidentally change the pointer’s mutability, nor cast the pointer to other types like usize.

Example

let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
let _ = ptr as *const i32;
let _ = mut_ptr as *mut i32;

Use instead:

let ptr: *const u32 = &42_u32;
let mut_ptr: *mut u32 = &mut 42_u32;
let _ = ptr.cast::<i32>();
let _ = mut_ptr.cast::<i32>();

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.51.0

What it does

Checks for as casts between raw pointers that change their constness, namely *const T to *mut T and *mut T to *const T.

Why is this bad?

Though as casts between raw pointers are not terrible, pointer::cast_mut and pointer::cast_const are safer because they cannot accidentally cast the pointer to another type. Or, when null pointers are involved, null() and null_mut() can be used directly.

Example

let ptr: *const u32 = &42_u32;
let mut_ptr = ptr as *mut u32;
let ptr = mut_ptr as *const u32;
let ptr1 = std::ptr::null::<u32>() as *mut u32;
let ptr2 = std::ptr::null_mut::<u32>() as *const u32;
let ptr3 = std::ptr::null::<u32>().cast_mut();
let ptr4 = std::ptr::null_mut::<u32>().cast_const();

Use instead:

let ptr: *const u32 = &42_u32;
let mut_ptr = ptr.cast_mut();
let ptr = mut_ptr.cast_const();
let ptr1 = std::ptr::null_mut::<u32>();
let ptr2 = std::ptr::null::<u32>();
let ptr3 = std::ptr::null_mut::<u32>();
let ptr4 = std::ptr::null::<u32>();
Applicability: MachineApplicable(?)
Added in: 1.72.0

What it does

Checks whether any field of the struct is prefixed with an _ (underscore) and also marked pub (public)

Why is this bad?

Fields prefixed with an _ are inferred as unused, which suggests it should not be marked as pub, because marking it as pub infers it will be used.

Example

struct FileHandle {
    pub _descriptor: usize,
}

Use instead:

struct FileHandle {
    _descriptor: usize,
}

OR

struct FileHandle {
    pub descriptor: usize,
}

Configuration

  • pub-underscore-fields-behavior: Lint “public” fields in a struct that are prefixed with an underscore based on their exported visibility, or whether they are marked as “pub”.

    (default: "PubliclyExported")

Applicability: Unspecified(?)
Added in: 1.77.0

What it does

Restricts the usage of pub use ...

Why restrict this?

A project may wish to limit pub use instances to prevent unintentional exports, or to encourage placing exported items directly in public modules.

Example

pub mod outer {
    mod inner {
        pub struct Test {}
    }
    pub use inner::Test;
}

use outer::Test;

Use instead:

pub mod outer {
    pub struct Test {}
}

use outer::Test;
Applicability: Unspecified(?)
Added in: 1.62.0

What it does

Checks for usage of pub(<loc>) with in.

Why restrict this?

Consistency. Use it or don’t, just be consistent about it.

Also see the pub_without_shorthand lint for an alternative.

Example

pub(super) type OptBox<T> = Option<Box<T>>;

Use instead:

pub(in super) type OptBox<T> = Option<Box<T>>;
Applicability: MachineApplicable(?)
Added in: 1.72.0

What it does

Checks for usage of pub(<loc>) without in.

Note: As you cannot write a module’s path in pub(<loc>), this will only trigger on pub(super) and the like.

Why restrict this?

Consistency. Use it or don’t, just be consistent about it.

Also see the pub_with_shorthand lint for an alternative.

Example

pub(in super) type OptBox<T> = Option<Box<T>>;

Use instead:

pub(super) type OptBox<T> = Option<Box<T>>;
Applicability: MachineApplicable(?)
Added in: 1.72.0

What it does

Checks for expressions that use the question mark operator and rejects them.

Why restrict this?

Sometimes code wants to avoid the question mark operator because for instance a local block requires a macro to re-throw errors to attach additional information to the error.

Example

let result = expr?;

Could be written:

utility_macro!(expr);
Applicability: Unspecified(?)
Added in: 1.69.0

What it does

Checks for inclusive ranges where 1 is subtracted from the upper bound, e.g., x..=(y-1).

Why is this bad?

The code is more readable with an exclusive range like x..y.

Known problems

This will cause a warning that cannot be fixed if the consumer of the range only accepts a specific range type, instead of the generic RangeBounds trait (#3307).

Example

for i in x..=(y-1) {
    // ..
}

Use instead:

for i in x..y {
    // ..
}
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for exclusive ranges where 1 is added to the upper bound, e.g., x..(y+1).

Why is this bad?

The code is more readable with an inclusive range like x..=y.

Known problems

Will add unnecessary pair of parentheses when the expression is not wrapped in a pair but starts with an opening parenthesis and ends with a closing one. I.e., let _ = (f()+1)..(f()+1) results in let _ = ((f()+1)..=f()).

Also in many cases, inclusive ranges are still slower to run than exclusive ranges, because they essentially add an extra branch that LLVM may fail to hoist out of the loop.

This will cause a warning that cannot be fixed if the consumer of the range only accepts a specific range type, instead of the generic RangeBounds trait (#3307).

Example

for i in x..(y+1) {
    // ..
}

Use instead:

for i in x..=y {
    // ..
}
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for Rc<T> and Arc<T> when T is a mutable buffer type such as String or Vec.

Why restrict this?

Expressions such as Rc<String> usually have no advantage over Rc<str>, since it is larger and involves an extra level of indirection, and doesn’t implement Borrow<str>.

While mutating a buffer type would still be possible with Rc::get_mut(), it only works if there are no additional references yet, which usually defeats the purpose of enclosing it in a shared ownership type. Instead, additionally wrapping the inner type with an interior mutable container (such as RefCell or Mutex) would normally be used.

Known problems

This pattern can be desirable to avoid the overhead of a RefCell or Mutex for cases where mutation only happens before there are any additional references.

Example

fn foo(interned: Rc<String>) { ... }

Better:

fn foo(interned: Rc<str>) { ... }

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

Applicability: Unspecified(?)
Added in: 1.48.0

What it does

Checks for Rc<Mutex<T>>.

Why restrict this?

Rc is used in single thread and Mutex is used in multi thread. Consider using Rc<RefCell<T>> in single thread or Arc<Mutex<T>> in multi thread.

Known problems

Sometimes combining generic types can lead to the requirement that a type use Rc in conjunction with Mutex. We must consider those cases false positives, but alas they are quite hard to rule out. Luckily they are also rare.

Example

use std::rc::Rc;
use std::sync::Mutex;
fn foo(interned: Rc<Mutex<i32>>) { ... }

Better:

use std::rc::Rc;
use std::cell::RefCell
fn foo(interned: Rc<RefCell<i32>>) { ... }

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

Applicability: Unspecified(?)
Added in: 1.55.0

What it does

This lint catches reads into a zero-length Vec. Especially in the case of a call to with_capacity, this lint warns that read gets the number of bytes from the Vec’s length, not its capacity.

Why is this bad?

Reading zero bytes is almost certainly not the intended behavior.

Known problems

In theory, a very unusual read implementation could assign some semantic meaning to zero-byte reads. But it seems exceptionally unlikely that code intending to do a zero-byte read would allocate a Vec for it.

Example

use std::io;
fn foo<F: io::Read>(mut f: F) {
    let mut data = Vec::with_capacity(100);
    f.read(&mut data).unwrap();
}

Use instead:

use std::io;
fn foo<F: io::Read>(mut f: F) {
    let mut data = Vec::with_capacity(100);
    data.resize(100, 0);
    f.read(&mut data).unwrap();
}
Applicability: MaybeIncorrect(?)
Added in: 1.63.0

What it does

Checks for a redundant clone() (and its relatives) which clones an owned value that is going to be dropped without further use.

Why is this bad?

It is not always possible for the compiler to eliminate useless allocations and deallocations generated by redundant clone()s.

Known problems

False-negatives: analysis performed by this lint is conservative and limited.

Example

{
    let x = Foo::new();
    call(x.clone());
    call(x.clone()); // this can just pass `x`
}

["lorem", "ipsum"].join(" ").to_string();

Path::new("/a/b").join("c").to_path_buf();
Applicability: MachineApplicable(?)
Added in: 1.32.0

What it does

Checks for closures which only invoke a method on the closure argument and can be replaced by referencing the method directly.

Why is this bad?

It’s unnecessary to create the closure.

Example

Some('a').map(|s| s.to_uppercase());

may be rewritten as

Some('a').map(char::to_uppercase);
Applicability: MachineApplicable(?)
Added in: 1.35.0

What it does

Checks for else blocks that can be removed without changing semantics.

Why is this bad?

The else block adds unnecessary indentation and verbosity.

Known problems

Some may prefer to keep the else block for clarity.

Example

fn my_func(count: u32) {
    if count == 0 {
        print!("Nothing to do");
        return;
    } else {
        print!("Moving on...");
    }
}

Use instead:

fn my_func(count: u32) {
    if count == 0 {
        print!("Nothing to do");
        return;
    }
    print!("Moving on...");
}
Applicability: MachineApplicable(?)
Added in: 1.50.0

What it does

Checks for feature names with prefix use-, with- or suffix -support

Why is this bad?

These prefixes and suffixes have no significant meaning.

Example

[features]
default = ["use-abc", "with-def", "ghi-support"]
use-abc = []  // redundant
with-def = []   // redundant
ghi-support = []   // redundant

Use instead:

[features]
default = ["abc", "def", "ghi"]
abc = []
def = []
ghi = []
Applicability: Unspecified(?)
Added in: 1.57.0

What it does

Checks for items declared pub(crate) that are not crate visible because they are inside a private module.

Why is this bad?

Writing pub(crate) is misleading when it’s redundant due to the parent module’s visibility.

Example

mod internal {
    pub(crate) fn internal_fn() { }
}

This function is not visible outside the module and it can be declared with pub or private visibility

mod internal {
    pub fn internal_fn() { }
}
Applicability: MachineApplicable(?)
Added in: 1.44.0

What it does

Warns about needless / redundant type annotations.

Why restrict this?

Code without type annotations is shorter and in most cases more idiomatic and easier to modify.

Limitations

This lint doesn’t support:

  • Generics
  • Refs returned from anything else than a MethodCall
  • Complex types (tuples, arrays, etc…)
  • Path to anything else than a primitive type.

Example

let foo: String = String::new();

Use instead:

let foo = String::new();
Applicability: Unspecified(?)
Added in: 1.72.0

What it does

Checks for casts of references to pointer using as and suggests std::ptr::from_ref and std::ptr::from_mut instead.

Why is this bad?

Using as casts may result in silently changing mutability or type.

Example

let a_ref = &1;
let a_ptr = a_ref as *const _;

Use instead:

let a_ref = &1;
let a_ptr = std::ptr::from_ref(a_ref);
Applicability: MachineApplicable(?)
Added in: 1.78.0

What it does

Checks for ref bindings which create a reference to a reference.

Why is this bad?

The address-of operator at the use site is clearer about the need for a reference.

Example

let x = Some("");
if let Some(ref x) = x {
    // use `x` here
}

Use instead:

let x = Some("");
if let Some(x) = x {
    // use `&x` here
}
Applicability: MachineApplicable(?)
Added in: 1.54.0

What it does

Warns when a function signature uses &Option<T> instead of Option<&T>.

Why is this bad?

More flexibility, better memory optimization, and more idiomatic Rust code.

&Option<T> in a function signature breaks encapsulation because the caller must own T and move it into an Option to call with it. When returned, the owner must internally store it as Option<T> in order to return it. At a lower level, &Option<T> points to memory with the presence bit flag plus the T value, whereas Option<&T> is usually optimized to a single pointer, so it may be more optimal.

See this YouTube video by Logan Smith for an in-depth explanation of why this is important.

Known problems

This lint recommends changing the function signatures, but it cannot automatically change the function calls or the function implementations.

Example

// caller uses  foo(&opt)
fn foo(a: &Option<String>) {}
fn bar(&self) -> &Option<String> { &None }

Use instead:

// caller should use  `foo1(opt.as_ref())`
fn foo1(a: Option<&String>) {}
// better yet, use string slice  `foo2(opt.as_deref())`
fn foo2(a: Option<&str>) {}
fn bar(&self) -> Option<&String> { None }

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

Applicability: Unspecified(?)
Added in: 1.83.0

What it does

Checks for usage of &Option<&T>.

Why is this bad?

Since & is Copy, it’s useless to have a reference on Option<&T>.

Known problems

It may be irrelevant to use this lint on public API code as it will make a breaking change to apply it.

Example

let x: &Option<&u32> = &Some(&0u32);

Use instead:

let x: Option<&u32> = Some(&0u32);
Applicability: MaybeIncorrect(?)
Added in: 1.49.0

What it does

Checks for usages of the ref keyword.

Why restrict this?

The ref keyword can be confusing for people unfamiliar with it, and often it is more concise to use & instead.

Example

let opt = Some(5);
if let Some(ref foo) = opt {}

Use instead:

let opt = Some(5);
if let Some(foo) = &opt {}
Applicability: Unspecified(?)
Added in: 1.71.0

What it does

Lints when the name of function parameters from trait impl is different than its default implementation.

Why restrict this?

Using the default name for parameters of a trait method is more consistent.

Example

struct A(u32);

impl PartialEq for A {
    fn eq(&self, b: &Self) -> bool {
        self.0 == b.0
    }
}

Use instead:

struct A(u32);

impl PartialEq for A {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

Configuration

  • allow-renamed-params-for: List of trait paths to ignore when checking renamed function parameters.

Example

allow-renamed-params-for = [ "std::convert::From" ]

Noteworthy

  • By default, the following traits are ignored: From, TryFrom, FromStr

  • ".." can be used as part of the list to indicate that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value.

    (default: ["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"])

Applicability: Unspecified(?)
Added in: 1.80.0

What it does

Checks for unnecessary ‘..’ pattern binding on struct when all fields are explicitly matched.

Why restrict this?

Correctness and readability. It’s like having a wildcard pattern after matching all enum variants explicitly.

Example

let a = A { a: 5 };

match a {
    A { a: 5, .. } => {},
    _ => {},
}

Use instead:

match a {
    A { a: 5 } => {},
    _ => {},
}
Applicability: Unspecified(?)
Added in: 1.43.0

What it does

Detect functions that end with Option::and_then or Result::and_then, and suggest using a question mark (?) instead.

Why is this bad?

The and_then method is used to chain a computation that returns an Option or a Result. This can be replaced with the ? operator, which is more concise and idiomatic.

Example

fn test(opt: Option<i32>) -> Option<i32> {
    opt.and_then(|n| {
        if n > 1 {
            Some(n + 1)
        } else {
            None
       }
    })
}

Use instead:

fn test(opt: Option<i32>) -> Option<i32> {
    let n = opt?;
    if n > 1 {
        Some(n + 1)
    } else {
        None
    }
}
Applicability: MachineApplicable(?)
Added in: 1.86.0

What it does

This lint warns when a method returning Self doesn’t have the #[must_use] attribute.

Why is this bad?

Methods returning Self often create new values, having the #[must_use] attribute prevents users from “forgetting” to use the newly created value.

The #[must_use] attribute can be added to the type itself to ensure that instances are never forgotten. Functions returning a type marked with #[must_use] will not be linted, as the usage is already enforced by the type attribute.

Limitations

This lint is only applied on methods taking a self argument. It would be mostly noise if it was added on constructors for example.

Example

pub struct Bar;
impl Bar {
    // Missing attribute
    pub fn bar(&self) -> Self {
        Self
    }
}

Use instead:

// It's better to have the `#[must_use]` attribute on the method like this:
pub struct Bar;
impl Bar {
    #[must_use]
    pub fn bar(&self) -> Self {
        Self
    }
}

// Or on the type definition like this:
#[must_use]
pub struct Bar;
impl Bar {
    pub fn bar(&self) -> Self {
        Self
    }
}
Applicability: Unspecified(?)
Added in: 1.59.0

What it does

Checks for consecutive ifs with the same function call.

Why is this bad?

This is probably a copy & paste error. Despite the fact that function can have side effects and if works as intended, such an approach is implicit and can be considered a “code smell”.

Example

if foo() == bar {
    …
} else if foo() == bar {
    …
}

This probably should be:

if foo() == bar {
    …
} else if foo() == baz {
    …
}

or if the original code was not a typo and called function mutates a state, consider move the mutation out of the if condition to avoid similarity to a copy & paste error:

let first = foo();
if first == bar {
    …
} else {
    let second = foo();
    if second == bar {
    …
    }
}
Applicability: Unspecified(?)
Added in: 1.41.0

What it does

It lints if a struct has two methods with the same name: one from a trait, another not from a trait.

Why restrict this?

Confusing.

Example

trait T {
    fn foo(&self) {}
}

struct S;

impl T for S {
    fn foo(&self) {}
}

impl S {
    fn foo(&self) {}
}
Applicability: Unspecified(?)
Added in: 1.57.0

What it does

Checks that module layout uses only mod.rs files.

Why restrict this?

Having multiple module layout styles in a project can be confusing.

Example

src/
  stuff/
    stuff_files.rs
  stuff.rs
  lib.rs

Use instead:

src/
  stuff/
    stuff_files.rs
    mod.rs
  lib.rs
Applicability: Unspecified(?)
Added in: 1.57.0

What it does

Looks for blocks of expressions and fires if the last expression returns () but is not followed by a semicolon.

Why is this bad?

The semicolon might be optional but when extending the block with new code, it doesn’t require a change in previous last line.

Example

fn main() {
    println!("Hello world")
}

Use instead:

fn main() {
    println!("Hello world");
}
Applicability: MachineApplicable(?)
Added in: 1.52.0

What it does

Suggests moving the semicolon after a block to the inside of the block, after its last expression.

Why restrict this?

For consistency it’s best to have the semicolon inside/outside the block. Either way is fine and this lint suggests inside the block. Take a look at semicolon_outside_block for the other alternative.

Example

unsafe { f(x) };

Use instead:

unsafe { f(x); }

Configuration

  • semicolon-inside-block-ignore-singleline: Whether to lint only if it’s multiline.

    (default: false)

Applicability: MachineApplicable(?)
Added in: 1.68.0

What it does

Suggests moving the semicolon from a block’s final expression outside of the block.

Why restrict this?

For consistency it’s best to have the semicolon inside/outside the block. Either way is fine and this lint suggests outside the block. Take a look at semicolon_inside_block for the other alternative.

Example

unsafe { f(x); }

Use instead:

unsafe { f(x) };

Configuration

  • semicolon-outside-block-ignore-multiline: Whether to lint only if it’s singleline.

    (default: false)

Applicability: MachineApplicable(?)
Added in: 1.68.0

What it does

Warns if literal suffixes are separated by an underscore. To enforce separated literal suffix style, see the unseparated_literal_suffix lint.

Why restrict this?

Suffix style should be consistent.

Example

123832_i32

Use instead:

123832i32
Applicability: MachineApplicable(?)
Added in: 1.58.0

What it does

Checks for usage of contains to see if a value is not present in a set like HashSet or BTreeSet, followed by an insert.

Why is this bad?

Using just insert and checking the returned bool is more efficient.

Known problems

In case the value that wants to be inserted is borrowed and also expensive or impossible to clone. In such a scenario, the developer might want to check with contains before inserting, to avoid the clone. In this case, it will report a false positive.

Example

use std::collections::HashSet;
let mut set = HashSet::new();
let value = 5;
if !set.contains(&value) {
    set.insert(value);
    println!("inserted {value:?}");
}

Use instead:

use std::collections::HashSet;
let mut set = HashSet::new();
let value = 5;
if set.insert(&value) {
    println!("inserted {value:?}");
}
Applicability: Unspecified(?)
Added in: 1.81.0

What it does

Checks for bindings that shadow other bindings already in scope, while reusing the original value.

Why restrict this?

Some argue that name shadowing like this hurts readability, because a value may be bound to different things depending on position in the code.

See also shadow_same and shadow_unrelated for other restrictions on shadowing.

Example

let x = 2;
let x = x + 1;

use different variable name:

let x = 2;
let y = x + 1;
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.

Why restrict this?

To require that what are formally distinct variables be given distinct names.

See also shadow_reuse and shadow_unrelated for other restrictions on shadowing.

Example

let x = &x;

Use instead:

let y = &x; // use different variable name
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for bindings that shadow other bindings already in scope, either without an initialization or with one that does not even use the original value.

Why restrict this?

Shadowing a binding with a closely related one is part of idiomatic Rust, but shadowing a binding by accident with an unrelated one may indicate a mistake.

Additionally, name shadowing in general can hurt readability, especially in large code bases, because it is easy to lose track of the active binding at any place in the code. If linting against all shadowing is desired, you may wish to use the shadow_same and shadow_reuse lints as well.

Example

let x = y;
let x = z; // shadows the earlier binding

Use instead:

let x = y;
let w = z; // use different variable name
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for #[should_panic] attributes without specifying the expected panic message.

Why is this bad?

The expected panic message should be specified to ensure that the test is actually panicking with the expected message, and not another unrelated panic.

Example

fn random() -> i32 { 0 }

#[should_panic]
#[test]
fn my_test() {
    let _ = 1 / random();
}

Use instead:

fn random() -> i32 { 0 }

#[should_panic = "attempt to divide by zero"]
#[test]
fn my_test() {
    let _ = 1 / random();
}
Applicability: HasPlaceholders(?)
Added in: 1.74.0

What it does

Checks for temporaries returned from function calls in a match scrutinee that have the clippy::has_significant_drop attribute.

Why is this bad?

The clippy::has_significant_drop attribute can be added to types whose Drop impls have an important side-effect, such as unlocking a mutex, making it important for users to be able to accurately understand their lifetimes. When a temporary is returned in a function call in a match scrutinee, its lifetime lasts until the end of the match block, which may be surprising.

For Mutexes this can lead to a deadlock. This happens when the match scrutinee uses a function call that returns a MutexGuard and then tries to lock again in one of the match arms. In that case the MutexGuard in the scrutinee will not be dropped until the end of the match block and thus will not unlock.

Example

let mutex = Mutex::new(State {});

match mutex.lock().unwrap().foo() {
    true => {
        mutex.lock().unwrap().bar(); // Deadlock!
    }
    false => {}
};

println!("All done!");

Use instead:

let mutex = Mutex::new(State {});

let is_foo = mutex.lock().unwrap().foo();
match is_foo {
    true => {
        mutex.lock().unwrap().bar();
    }
    false => {}
};

println!("All done!");
Applicability: MaybeIncorrect(?)
Added in: 1.60.0

What it does

Searches for elements marked with #[clippy::has_significant_drop] that could be early dropped but are in fact dropped at the end of their scopes. In other words, enforces the “tightening” of their possible lifetimes.

Why is this bad?

Elements marked with #[clippy::has_significant_drop] are generally synchronizing primitives that manage shared resources, as such, it is desired to release them as soon as possible to avoid unnecessary resource contention.

Example

fn main() {
  let lock = some_sync_resource.lock();
  let owned_rslt = lock.do_stuff_with_resource();
  // Only `owned_rslt` is needed but `lock` is still held.
  do_heavy_computation_that_takes_time(owned_rslt);
}

Use instead:

fn main() {
    let owned_rslt = some_sync_resource.lock().do_stuff_with_resource();
    do_heavy_computation_that_takes_time(owned_rslt);
}
Applicability: MaybeIncorrect(?)
Added in: 1.69.0

What it does

Checks for names that are very similar and thus confusing.

Note: this lint looks for similar names throughout each scope. To allow it, you need to allow it on the scope level, not on the name that is reported.

Why is this bad?

It’s hard to distinguish between names that differ only by a single character.

Example

let checked_exp = something;
let checked_expr = something_else;
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for functions that are only used once. Does not lint tests.

Why restrict this?

If a function is only used once (perhaps because it used to be used more widely), then the code could be simplified by moving that function’s code into its caller.

However, there are reasons not to do this everywhere:

  • Splitting a large function into multiple parts often improves readability by giving names to its parts.
  • A function’s signature might serve a necessary purpose, such as constraining the type of a closure passed to it.
  • Generic functions might call non-generic functions to reduce duplication in the produced machine code.

If this lint is used, prepare to #[allow] it a lot.

Example

pub fn a<T>(t: &T)
where
    T: AsRef<str>,
{
    a_inner(t.as_ref())
}

fn a_inner(t: &str) {
    /* snip */
}

Use instead:

pub fn a<T>(t: &T)
where
    T: AsRef<str>,
{
    let t = t.as_ref();
    /* snip */
}

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

Applicability: Unspecified(?)
Added in: 1.72.0

What it does

Checks for lifetimes with names which are one character long.

Why restrict this?

A single character is likely not enough to express the purpose of a lifetime. Using a longer name can make code easier to understand.

Known problems

Rust programmers and learning resources tend to use single character lifetimes, so this lint is at odds with the ecosystem at large. In addition, the lifetime’s purpose may be obvious or, rarely, expressible in one character.

Example

struct DiagnosticCtx<'a> {
    source: &'a str,
}

Use instead:

struct DiagnosticCtx<'src> {
    source: &'src str,
}
Applicability: Unspecified(?)
Added in: 1.60.0

What it does

Checks for string methods that receive a single-character str as an argument, e.g., _.split("x").

Why is this bad?

While this can make a perf difference on some systems, benchmarks have proven inconclusive. But at least using a char literal makes it clear that we are looking at a single character.

Known problems

Does not catch multi-byte unicode characters. This is by design, on many machines, splitting by a non-ascii char is actually slower. Please do your own measurements instead of relying solely on the results of this lint.

Example

_.split("x");

Use instead:

_.split('x');
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for matches with two arms where an if let else will usually suffice.

Why is this bad?

Just readability – if let nests less than a match.

Known problems

Personal style preferences may differ.

Example

Using match:

match x {
    Some(ref foo) => bar(foo),
    _ => bar(&other_ref),
}

Using if let with else:

if let Some(ref foo) = x {
    bar(foo);
} else {
    bar(&other_ref);
}
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for functions with method calls to .map(_) on an arg of type Option as the outermost expression.

Why is this bad?

Taking and returning an Option<T> may require additional Some(_) and unwrap if all you have is a T.

Example

fn double(param: Option<u32>) -> Option<u32> {
    param.map(|x| x * 2)
}

Use instead:

fn double(param: u32) -> u32 {
    param * 2
}
Applicability: Unspecified(?)
Added in: 1.86.0

What it does

When sorting primitive values (integers, bools, chars, as well as arrays, slices, and tuples of such items), it is typically better to use an unstable sort than a stable sort.

Why is this bad?

Typically, using a stable sort consumes more memory and cpu cycles. Because values which compare equal are identical, preserving their relative order (the guarantee that a stable sort provides) means nothing, while the extra costs still apply.

Known problems

As pointed out in issue #8241, a stable sort can instead be significantly faster for certain scenarios (eg. when a sorted vector is extended with new data and resorted).

For more information and benchmarking results, please refer to the issue linked above.

Example

let mut vec = vec![2, 1, 3];
vec.sort();

Use instead:

let mut vec = vec![2, 1, 3];
vec.sort_unstable();
Applicability: MachineApplicable(?)
Added in: 1.47.0

What it does

Finds items imported through std when available through alloc.

Why restrict this?

Crates which have no_std compatibility and require alloc may wish to ensure types are imported from alloc to ensure disabling std does not cause the crate to fail to compile. This lint is also useful for crates migrating to become no_std compatible.

Example

use std::vec::Vec;

Use instead:

use alloc::vec::Vec;
Applicability: MachineApplicable(?)
Added in: 1.64.0

What it does

Finds items imported through std when available through core.

Why restrict this?

Crates which have no_std compatibility may wish to ensure types are imported from core to ensure disabling std does not cause the crate to fail to compile. This lint is also useful for crates migrating to become no_std compatible.

Example

use std::hash::Hasher;

Use instead:

use core::hash::Hasher;
Applicability: MachineApplicable(?)
Added in: 1.64.0

What it does

Checks for usages of str.trim().split("\n") and str.trim().split("\r\n").

Why is this bad?

Hard-coding the line endings makes the code less compatible. str.lines should be used instead.

Example

"some\ntext\nwith\nnewlines\n".trim().split('\n');

Use instead:

"some\ntext\nwith\nnewlines\n".lines();

Known Problems

This lint cannot detect if the split is intentionally restricted to a single type of newline ("\n" or "\r\n"), for example during the parsing of a specific file format in which precisely one newline type is valid.

Applicability: MaybeIncorrect(?)
Added in: 1.77.0

What it does

This lint checks for .to_string() method calls on values of type &str.

Why restrict this?

The to_string method is also used on other types to convert them to a string. When called on a &str it turns the &str into the owned variant String, which can be more specifically expressed with .to_owned().

Example

let _ = "str".to_string();

Use instead:

let _ = "str".to_owned();
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for all instances of x + _ where x is of type String, but only if string_add_assign does not match.

Why restrict this?

This particular Add implementation is asymmetric (the other operand need not be String, but x does), while addition as mathematically defined is symmetric, and the String::push_str(_) function is a perfectly good replacement. Therefore, some dislike it and wish not to have it in their code.

That said, other people think that string addition, having a long tradition in other languages is actually fine, which is why we decided to make this particular lint allow by default.

Example

let x = "Hello".to_owned();
x + ", World";

Use instead:

let mut x = "Hello".to_owned();
x.push_str(", World");
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for string appends of the form x = x + y (without let!).

Why is this bad?

It’s not really bad, but some people think that the .push_str(_) method is more readable.

Example

let mut x = "Hello".to_owned();
x = x + ", World";

// More readable
x += ", World";
x.push_str(", World");
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for the as_bytes method called on string literals that contain only ASCII characters.

Why is this bad?

Byte string literals (e.g., b"foo") can be used instead. They are shorter but less discoverable than as_bytes().

Known problems

"str".as_bytes() and the suggested replacement of b"str" are not equivalent because they have different types. The former is &[u8] while the latter is &[u8; 3]. That means in general they will have a different set of methods and different trait implementations.

fn f(v: Vec<u8>) {}

f("...".as_bytes().to_owned()); // works
f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec<u8>

fn g(r: impl std::io::Read) {}

g("...".as_bytes()); // works
g(b"..."); // does not work

The actual equivalent of "str".as_bytes() with the same type is not b"str" but &b"str"[..], which is a great deal of punctuation and not more readable than a function call.

Example

let bstr = "a byte string".as_bytes();

Use instead:

let bstr = b"a byte string";
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for <string_lit>.chars().any(|i| i == c).

Why is this bad?

It’s significantly slower than using a pattern instead, like matches!(c, '\\' | '.' | '+').

Despite this being faster, this is not perf as this is pretty common, and is a rather nice way to check if a char is any in a set. In any case, this restriction lint is available for situations where that additional performance is absolutely necessary.

Example

"\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c);

Use instead:

matches!(c, '\\' | '.' | '+' | '*' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' | '#' | '&' | '-' | '~');
Applicability: MachineApplicable(?)
Added in: 1.73.0

What it does

Checks for slice operations on strings

Why restrict this?

UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character counts and string indices. This may lead to panics, and should warrant some test cases containing wide UTF-8 characters. This lint is most useful in code that should avoid panics at all costs.

Known problems

Probably lots of false positives. If an index comes from a known valid position (e.g. obtained via char_indices over the same string), it is totally OK.

Example

&"Ölkanne"[1..];
Applicability: Unspecified(?)
Added in: 1.58.0

What it does

This lint checks for .to_string() method calls on values of type String.

Why restrict this?

The to_string method is also used on other types to convert them to a string. When called on a String it only clones the String, which can be more specifically expressed with .clone().

Example

let msg = String::from("Hello World");
let _ = msg.to_string();

Use instead:

let msg = String::from("Hello World");
let _ = msg.clone();
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for excessive use of bools in structs.

Why is this bad?

Excessive bools in a struct is often a sign that the type is being used to represent a state machine, which is much better implemented as an enum.

The reason an enum is better for state machines over structs is that enums more easily forbid invalid states.

Structs with too many booleans may benefit from refactoring into multi variant enums for better readability and API.

Example

struct S {
    is_pending: bool,
    is_processing: bool,
    is_finished: bool,
}

Use instead:

enum S {
    Pending,
    Processing,
    Finished,
}

Configuration

  • max-struct-bools: The maximum number of bool fields a struct can have

    (default: 3)

Applicability: Unspecified(?)
Added in: 1.43.0

What it does

Detects struct fields that are prefixed or suffixed by the same characters or the name of the struct itself.

Why is this bad?

Information common to all struct fields is better represented in the struct name.

Limitations

Characters with no casing will be considered when comparing prefixes/suffixes This applies to numbers and non-ascii characters without casing e.g. foo1 and foo2 is considered to have different prefixes (the prefixes are foo1 and foo2 respectively), as also bar螃, bar蟹

Example

struct Cake {
    cake_sugar: u8,
    cake_flour: u8,
    cake_eggs: u8
}

Use instead:

struct Cake {
    sugar: u8,
    flour: u8,
    eggs: u8
}

Configuration

  • struct-field-name-threshold: The minimum number of struct fields for the lints about field names to trigger

    (default: 3)

Applicability: Unspecified(?)
Added in: 1.75.0

What it does

Looks for floating-point expressions that can be expressed using built-in methods to improve both accuracy and performance.

Why is this bad?

Negatively impacts accuracy and performance.

Example

use std::f32::consts::E;

let a = 3f32;
let _ = (2f32).powf(a);
let _ = E.powf(a);
let _ = a.powf(1.0 / 2.0);
let _ = a.log(2.0);
let _ = a.log(10.0);
let _ = a.log(E);
let _ = a.powf(2.0);
let _ = a * 2.0 + 4.0;
let _ = if a < 0.0 {
    -a
} else {
    a
};
let _ = if a < 0.0 {
    a
} else {
    -a
};

is better expressed as

use std::f32::consts::E;

let a = 3f32;
let _ = a.exp2();
let _ = a.exp();
let _ = a.sqrt();
let _ = a.log2();
let _ = a.log10();
let _ = a.ln();
let _ = a.powi(2);
let _ = a.mul_add(2.0, 4.0);
let _ = a.abs();
let _ = -a.abs();
Applicability: MachineApplicable(?)
Added in: 1.43.0

What it does

Checks for unlikely usages of binary operators that are almost certainly typos and/or copy/paste errors, given the other usages of binary operators nearby.

Why is this bad?

They are probably bugs and if they aren’t then they look like bugs and you should add a comment explaining why you are doing such an odd set of operations.

Known problems

There may be some false positives if you are trying to do something unusual that happens to look like a typo.

Example

struct Vec3 {
    x: f64,
    y: f64,
    z: f64,
}

impl Eq for Vec3 {}

impl PartialEq for Vec3 {
    fn eq(&self, other: &Self) -> bool {
        // This should trigger the lint because `self.x` is compared to `other.y`
        self.x == other.y && self.y == other.y && self.z == other.z
    }
}

Use instead:

// same as above except:
impl PartialEq for Vec3 {
    fn eq(&self, other: &Self) -> bool {
        // Note we now compare other.x to self.x
        self.x == other.x && self.y == other.y && self.z == other.z
    }
}
Applicability: MachineApplicable(?)
Added in: 1.50.0

What it does

Warns for a Bitwise XOR (^) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal.

Why restrict this?

It’s most probably a typo and may lead to unexpected behaviours.

Example

let x = 3_i32 ^ 4_i32;

Use instead:

let x = 3_i32.pow(4);
Applicability: MaybeIncorrect(?)
Added in: 1.67.0

What it does

Triggers when a testing function (marked with the #[test] attribute) isn’t inside a testing module (marked with #[cfg(test)]).

Why restrict this?

The idiomatic (and more performant) way of writing tests is inside a testing module (flagged with #[cfg(test)]), having test functions outside of this module is confusing and may lead to them being “hidden”.

Example

#[test]
fn my_cool_test() {
    // [...]
}

#[cfg(test)]
mod tests {
    // [...]
}

Use instead:

#[cfg(test)]
mod tests {
    #[test]
    fn my_cool_test() {
        // [...]
    }
}
Applicability: Unspecified(?)
Added in: 1.70.0

What it does

Checks for usage of todo!.

Why restrict this?

The todo! macro indicates the presence of unfinished code, so it should not be present in production code.

Example

todo!();

Finish the implementation, or consider marking it as explicitly unimplemented.

unimplemented!();
Applicability: Unspecified(?)
Added in: 1.40.0

What it does

Checks if the first paragraph in the documentation of items listed in the module page is too long.

Why is this bad?

Documentation will show the first paragraph of the docstring in the summary page of a module. Having a nice, short summary in the first paragraph is part of writing good docs.

Example

/// A very short summary.
/// A much longer explanation that goes into a lot more detail about
/// how the thing works, possibly with doclinks and so one,
/// and probably spanning a many rows.
struct Foo {}

Use instead:

/// A very short summary.
///
/// A much longer explanation that goes into a lot more detail about
/// how the thing works, possibly with doclinks and so one,
/// and probably spanning a many rows.
struct Foo {}
Applicability: MachineApplicable(?)
Added in: 1.82.0

What it does

Checks for functions with a large amount of lines.

Why is this bad?

Functions with a lot of lines are harder to understand due to having to look at a larger amount of code to understand what the function is doing. Consider splitting the body of the function into multiple functions.

Example

fn im_too_long() {
    println!("");
    // ... 100 more LoC
    println!("");
}

Configuration

  • too-many-lines-threshold: The maximum number of lines a function or method can have

    (default: 100)

Applicability: Unspecified(?)
Added in: 1.34.0

What it does

Displays a warning when a struct with a trailing zero-sized array is declared without a repr attribute.

Why is this bad?

Zero-sized arrays aren’t very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjunction with manual allocation to make it easy to compute the offset of the array). Either way, #[repr(C)] (or another repr attribute) is needed.

Example

struct RarelyUseful {
    some_field: u32,
    last: [u32; 0],
}

Use instead:

#[repr(C)]
struct MoreOftenUseful {
    some_field: usize,
    last: [u32; 0],
}
Applicability: Unspecified(?)
Added in: 1.58.0

What it does

Checks for cases where generics or trait objects are being used and multiple syntax specifications for trait bounds are used simultaneously.

Why is this bad?

Duplicate bounds makes the code less readable than specifying them only once.

Example

fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}

Use instead:

fn func<T: Clone + Default>(arg: T) {}

// or

fn func<T>(arg: T) where T: Clone + Default {}
fn foo<T: Default + Default>(bar: T) {}

Use instead:

fn foo<T: Default>(bar: T) {}
fn foo<T>(bar: T) where T: Default + Default {}

Use instead:

fn foo<T>(bar: T) where T: Default {}
Applicability: MachineApplicable(?)
Added in: 1.47.0

What it does

Checks for transmutes from a pointer to a pointer, or from a reference to a reference.

Why is this bad?

Transmutes are dangerous, and these can instead be written as casts.

Example

let ptr = &1u32 as *const u32;
unsafe {
    // pointer-to-pointer transmute
    let _: *const f32 = std::mem::transmute(ptr);
    // ref-ref transmute
    let _: &f32 = std::mem::transmute(&1u32);
}
// These can be respectively written:
let _ = ptr as *const f32;
let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
Applicability: MaybeIncorrect(?)
Added in: pre 1.29.0

What it does

Checks for transmutes between types which do not have a representation defined relative to each other.

Why is this bad?

The results of such a transmute are not defined.

Known problems

This lint has had multiple problems in the past and was moved to nursery. See issue #8496 for more details.

Example

struct Foo<T>(u32, T);
let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };

Use instead:

#[repr(C)]
struct Foo<T>(u32, T);
let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
Applicability: Unspecified(?)
Added in: 1.60.0

What it does

Checks for trivial regex creation (with Regex::new, RegexBuilder::new, or RegexSet::new).

Why is this bad?

Matching the regex can likely be replaced by == or str::starts_with, str::ends_with or std::contains or other str methods.

Known problems

If the same regex is going to be applied to multiple inputs, the precomputations done by Regex construction can give significantly better performance than any of the str-based methods.

Example

Regex::new("^foobar")

Use instead:

str::starts_with("foobar")
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for functions taking arguments by reference, where the argument type is Copy and small enough to be more efficient to always pass by value.

Why is this bad?

In many calling conventions instances of structs will be passed through registers if they fit into two or less general purpose registers.

Known problems

This lint is target register size dependent, it is limited to 32-bit to try and reduce portability problems between 32 and 64-bit, but if you are compiling for 8 or 16-bit targets then the limit will be different.

The configuration option trivial_copy_size_limit can be set to override this limit for a project.

This lint attempts to allow passing arguments by reference if a reference to that argument is returned. This is implemented by comparing the lifetime of the argument and return value for equality. However, this can cause false positives in cases involving multiple lifetimes that are bounded by each other.

Also, it does not take account of other similar cases where getting memory addresses matters; namely, returning the pointer to the argument in question, and passing the argument, as both references and pointers, to a function that needs the memory address. For further details, refer to this issue that explains a real case in which this false positive led to an undefined behavior introduced with unsafe code.

Example

fn foo(v: &u32) {}

Use instead:

fn foo(v: u32) {}

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

  • trivial-copy-size-limit: The maximum size (in bytes) to consider a Copy type for passing by value instead of by reference.

    (default: target_pointer_width * 2)

Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for usage of Err(x)?.

Why restrict this?

The ? operator is designed to allow calls that can fail to be easily chained. For example, foo()?.bar() or foo(bar()?). Because Err(x)? can’t be used that way (it will always return), it is more clear to write return Err(x).

Example

fn foo(fail: bool) -> Result<i32, String> {
    if fail {
      Err("failed")?;
    }
    Ok(0)
}

Could be written:

fn foo(fail: bool) -> Result<i32, String> {
    if fail {
      return Err("failed".into());
    }
    Ok(0)
}
Applicability: MachineApplicable(?)
Added in: 1.38.0

What it does

Checks for tuple<=>array conversions that are not done with .into().

Why is this bad?

It may be unnecessary complexity. .into() works for converting tuples<=> arrays of up to 12 elements and conveys the intent more clearly, while also leaving less room for hard to spot bugs!

Known issues

The suggested code may hide potential asymmetry in some cases. See #11085 for more info.

Example

let t1 = &[(1, 2), (3, 4)];
let v1: Vec<[u32; 2]> = t1.iter().map(|&(a, b)| [a, b]).collect();

Use instead:

let t1 = &[(1, 2), (3, 4)];
let v1: Vec<[u32; 2]> = t1.iter().map(|&t| t.into()).collect();

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: Unspecified(?)
Added in: 1.72.0

What it does

This lint warns about unnecessary type repetitions in trait bounds

Why is this bad?

Repeating the type for every bound makes the code less readable than combining the bounds

Example

pub fn foo<T>(t: T) where T: Copy, T: Clone {}

Use instead:

pub fn foo<T>(t: T) where T: Copy + Clone {}

Configuration

  • max-trait-bounds: The maximum number of bounds a trait can have to be linted

    (default: 3)

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: Unspecified(?)
Added in: 1.38.0

What it does

Lints subtraction between an Instant and a Duration.

Why is this bad?

Unchecked subtraction could cause underflow on certain platforms, leading to unintentional panics.

Example

let time_passed = Instant::now() - Duration::from_secs(5);

Use instead:

let time_passed = Instant::now().checked_sub(Duration::from_secs(5));

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.67.0

What it does

Checks for unsafe blocks and impls without a // SAFETY: comment explaining why the unsafe operations performed inside the block are safe.

Note the comment must appear on the line(s) preceding the unsafe block with nothing appearing in between. The following is ok:

foo(
    // SAFETY:
    // This is a valid safety comment
    unsafe { *x }
)

But neither of these are:

// SAFETY:
// This is not a valid safety comment
foo(
    /* SAFETY: Neither is this */ unsafe { *x },
);

Why restrict this?

Undocumented unsafe blocks and impls can make it difficult to read and maintain code. Writing out the safety justification may help in discovering unsoundness or bugs.

Example

use std::ptr::NonNull;
let a = &mut 42;

let ptr = unsafe { NonNull::new_unchecked(a) };

Use instead:

use std::ptr::NonNull;
let a = &mut 42;

// SAFETY: references are guaranteed to be non-null.
let ptr = unsafe { NonNull::new_unchecked(a) };

Configuration

  • accept-comment-above-attributes: Whether to accept a safety comment to be placed above the attributes for the unsafe block

    (default: true)

  • accept-comment-above-statement: Whether to accept a safety comment to be placed above the statement containing the unsafe block

    (default: true)

Applicability: Unspecified(?)
Added in: 1.58.0

What it does

Checks for string literals that contain Unicode in a form that is not equal to its NFC-recomposition.

Why is this bad?

If such a string is compared to another, the results may be surprising.

Example

You may not see it, but “à”“ and “à”“ aren’t the same string. The former when escaped is actually "a\u{300}" while the latter is "\u{e0}".

Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for usage of unimplemented!.

Why restrict this?

This macro, or panics in general, may be unwanted in production code.

Example

unimplemented!();
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

It detects references to uninhabited types, such as ! and warns when those are either dereferenced or returned from a function.

Why is this bad?

Dereferencing a reference to an uninhabited type would create an instance of such a type, which cannot exist. This constitutes undefined behaviour. Such a reference could have been created by unsafe code.

Example

The following function can return a reference to an uninhabited type (Infallible) because it uses unsafe code to create it. However, the user of such a function could dereference the return value and trigger an undefined behavior from safe code.

fn create_ref() -> &'static std::convert::Infallible {
    unsafe { std::mem::transmute(&()) }
}
Applicability: Unspecified(?)
Added in: 1.76.0

What it does

Checks for a return type containing a Box<T> where T implements Sized

The lint ignores Box<T> where T is larger than unnecessary_box_size, as returning a large T directly may be detrimental to performance.

Why is this bad?

It’s better to just return T in these cases. The caller may not need the value to be boxed, and it’s expensive to free the memory once the Box<T> been dropped.

Example

fn foo() -> Box<String> {
    Box::new(String::from("Hello, world!"))
}

Use instead:

fn foo() -> String {
    String::from("Hello, world!")
}

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

  • unnecessary-box-size: The byte size a T in Box<T> can have, below which it triggers the clippy::unnecessary_box lint

    (default: 128)

Applicability: Unspecified(?)
Added in: 1.70.0

What it does

Checks for Debug formatting ({:?}) applied to an OsStr or Path.

Why is this bad?

Rust doesn’t guarantee what Debug formatting looks like, and it could change in the future. OsStrs and Paths can be Display formatted using their display methods.

Furthermore, with Debug formatting, certain characters are escaped. Thus, a Debug formatted Path is less likely to be clickable.

Example

let path = Path::new("...");
println!("The path is {:?}", path);

Use instead:

let path = Path::new("…");
println!("The path is {}", path.display());
Applicability: Unspecified(?)
Added in: 1.87.0

What it does

Checks for usage of .collect::<Vec<String>>().join("") on iterators.

Why is this bad?

.collect::<String>() is more concise and might be more performant

Example

let vector = vec!["hello",  "world"];
let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
println!("{}", output);

The correct use would be:

let vector = vec!["hello",  "world"];
let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
println!("{}", output);

Known problems

While .collect::<String>() is sometimes more performant, there are cases where using .collect::<String>() over .collect::<Vec<String>>().join("") will prevent loop unrolling and will result in a negative performance impact.

Additionally, differences have been observed between aarch64 and x86_64 assembly output, with aarch64 tending to producing faster assembly in more cases when using .collect::<String>()

Applicability: MachineApplicable(?)
Added in: 1.61.0

What it does

Detects functions that are written to return &str that could return &'static str but instead return a &'a str.

Why is this bad?

This leaves the caller unable to use the &str as &'static str, causing unnecessary allocations or confusion. This is also most likely what you meant to write.

Example

impl MyType {
    fn returns_literal(&self) -> &str {
        "Literal"
    }
}

Use instead:

impl MyType {
    fn returns_literal(&self) -> &'static str {
        "Literal"
    }
}

Or, in case you may return a non-literal str in future:

impl MyType {
    fn returns_literal<'a>(&'a self) -> &'a str {
        "Literal"
    }
}
Applicability: MachineApplicable(?)
Added in: 1.84.0

What it does

Checks for // SAFETY: comments on safe code.

Why restrict this?

Safe code has no safety requirements, so there is no need to describe safety invariants.

Example

use std::ptr::NonNull;
let a = &mut 42;

// SAFETY: references are guaranteed to be non-null.
let ptr = NonNull::new(a).unwrap();

Use instead:

use std::ptr::NonNull;
let a = &mut 42;

let ptr = NonNull::new(a).unwrap();
Applicability: Unspecified(?)
Added in: 1.67.0

What it does

Checks for the doc comments of publicly visible safe functions and traits and warns if there is a # Safety section.

Why restrict this?

Safe functions and traits are safe to implement and therefore do not need to describe safety preconditions that users are required to uphold.

Examples

/// # Safety
///
/// This function should not be called before the horsemen are ready.
pub fn start_apocalypse_but_safely(u: &mut Universe) {
    unimplemented!();
}

The function is safe, so there shouldn’t be any preconditions that have to be explained for safety reasons.

/// This function should really be documented
pub fn start_apocalypse(u: &mut Universe) {
    unimplemented!();
}

Configuration

  • check-private-items: Whether to also run the listed lints on private items.

    (default: false)

Applicability: Unspecified(?)
Added in: 1.67.0

What it does

Checks for imports ending in ::{self}.

Why restrict this?

In most cases, this can be written much more cleanly by omitting ::{self}.

Known problems

Removing ::{self} will cause any non-module items at the same path to also be imported. This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt to detect this scenario and that is why it is a restriction lint.

Example

use std::io::{self};

Use instead:

use std::io;
Applicability: MaybeIncorrect(?)
Added in: 1.53.0

What it does

Checks for the presence of a semicolon at the end of a match or if statement evaluating to ().

Why is this bad?

The semicolon is not needed, and may be removed to avoid confusion and visual clutter.

Example

if a > 10 {
    println!("a is greater than 10");
};

Use instead:

if a > 10 {
    println!("a is greater than 10");
}
Applicability: MachineApplicable(?)
Added in: 1.86.0

What it does

Checks for initialization of an identical struct from another instance of the type, either by copying a base without setting any field or by moving all fields individually.

Why is this bad?

Readability suffers from unnecessary struct building.

Example

struct S { s: String }

let a = S { s: String::from("Hello, world!") };
let b = S { ..a };

Use instead:

struct S { s: String }

let a = S { s: String::from("Hello, world!") };
let b = a;

The struct literal S { ..a } in the assignment to b could be replaced with just a.

Known Problems

Has false positives when the base is a place expression that cannot be moved out of, see #10547.

Empty structs are ignored by the lint.

Applicability: MachineApplicable(?)
Added in: 1.70.0

What it does

Checks for private functions that only return Ok or Some.

Why is this bad?

It is not meaningful to wrap values when no None or Err is returned.

Known problems

There can be false positives if the function signature is designed to fit some external requirement.

Example

fn get_cool_number(a: bool, b: bool) -> Option<i32> {
    if a && b {
        return Some(50);
    }
    if a {
        Some(0)
    } else {
        Some(10)
    }
}

Use instead:

fn get_cool_number(a: bool, b: bool) -> i32 {
    if a && b {
        return 50;
    }
    if a {
        0
    } else {
        10
    }
}

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

Applicability: MaybeIncorrect(?)
Added in: 1.50.0

What it does

Checks for structure field patterns bound to wildcards.

Why restrict this?

Using .. instead is shorter and leaves the focus on the fields that are actually bound.

Example

let f = Foo { a: 0, b: 0, c: 0 };

match f {
    Foo { a: _, b: 0, .. } => {},
    Foo { a: _, b: _, c: _ } => {},
}

Use instead:

let f = Foo { a: 0, b: 0, c: 0 };

match f {
    Foo { b: 0, .. } => {},
    Foo { .. } => {},
}
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for unnested or-patterns, e.g., Some(0) | Some(2) and suggests replacing the pattern with a nested one, Some(0 | 2).

Another way to think of this is that it rewrites patterns in disjunctive normal form (DNF) into conjunctive normal form (CNF).

Why is this bad?

In the example above, Some is repeated, which unnecessarily complicates the pattern.

Example

fn main() {
    if let Some(0) | Some(2) = Some(0) {}
}

Use instead:

fn main() {
    if let Some(0 | 2) = Some(0) {}
}

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.46.0

What it does

Checks for usage of unreachable!.

Why restrict this?

This macro, or panics in general, may be unwanted in production code.

Example

unreachable!();
Applicability: Unspecified(?)
Added in: 1.40.0

What it does

Warns if a long integral or floating-point constant does not contain underscores.

Why is this bad?

Reading long numbers is difficult without separators.

Example

61864918973511

Use instead:

61_864_918_973_511

Configuration

  • unreadable-literal-lint-fractions: Should the fraction of a decimal be linted to include separators.

    (default: true)

Applicability: MaybeIncorrect(?)
Added in: pre 1.29.0

What it does

Checks for deriving serde::Deserialize on a type that has methods using unsafe.

Why is this bad?

Deriving serde::Deserialize will create a constructor that may violate invariants held by another constructor.

Example

use serde::Deserialize;

#[derive(Deserialize)]
pub struct Foo {
    // ..
}

impl Foo {
    pub fn new() -> Self {
        // setup here ..
    }

    pub unsafe fn parts() -> (&str, &str) {
        // assumes invariants hold
    }
}
Applicability: Unspecified(?)
Added in: 1.45.0

What it does

Warns if literal suffixes are not separated by an underscore. To enforce unseparated literal suffix style, see the separated_literal_suffix lint.

Why restrict this?

Suffix style should be consistent.

Example

123832i32

Use instead:

123832_i32
Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for functions that are declared async but have no .awaits inside of them.

Why is this bad?

Async functions with no async code create overhead, both mentally and computationally. Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which causes runtime overhead and hassle for the caller.

Example

async fn get_random_number() -> i64 {
    4 // Chosen by fair dice roll. Guaranteed to be random.
}
let number_future = get_random_number();

Use instead:

fn get_random_number_improved() -> i64 {
    4 // Chosen by fair dice roll. Guaranteed to be random.
}
let number_future = async { get_random_number_improved() };
Applicability: Unspecified(?)
Added in: 1.54.0

What it does

Checks for the creation of a peekable iterator that is never .peek()ed

Why is this bad?

Creating a peekable iterator without using any of its methods is likely a mistake, or just a leftover after a refactor.

Example

let collection = vec![1, 2, 3];
let iter = collection.iter().peekable();

for item in iter {
    // ...
}

Use instead:

let collection = vec![1, 2, 3];
let iter = collection.iter();

for item in iter {
    // ...
}
Applicability: Unspecified(?)
Added in: 1.65.0

What it does

Checks for calls to Result::ok() without using the returned Option.

Why is this bad?

Using Result::ok() may look like the result is checked like unwrap or expect would do but it only silences the warning caused by #[must_use] on the Result.

Example

some_function().ok();

Use instead:

let _ = some_function();
Applicability: MaybeIncorrect(?)
Added in: 1.82.0

What it does

Detects cases where a whole-number literal float is being rounded, using the floor, ceil, or round methods.

Why is this bad?

This is unnecessary and confusing to the reader. Doing this is probably a mistake.

Example

let x = 1f32.ceil();

Use instead:

let x = 1f32;
Applicability: MachineApplicable(?)
Added in: 1.63.0

What it does

Checks methods that contain a self argument but don’t use it

Why is this bad?

It may be clearer to define the method as an associated function instead of an instance method if it doesn’t require self.

Example

struct A;
impl A {
    fn method(&self) {}
}

Could be written:

struct A;
impl A {
    fn method() {}
}

Configuration

  • avoid-breaking-exported-api: Suppress lints whenever the suggested change would cause breakage for other crates.

    (default: true)

Applicability: Unspecified(?)
Added in: 1.40.0

What it does

Checks for use Trait where the Trait is only used for its methods and not referenced by a path directly.

Why is this bad?

Traits imported that aren’t used directly can be imported anonymously with use Trait as _. It is more explicit, avoids polluting the current scope with unused names and can be useful to show which imports are required for traits.

Example

use std::fmt::Write;

fn main() {
    let mut s = String::new();
    let _ = write!(s, "hello, world!");
    println!("{s}");
}

Use instead:

use std::fmt::Write as _;

fn main() {
    let mut s = String::new();
    let _ = write!(s, "hello, world!");
    println!("{s}");
}

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: 1.83.0

What it does

Checks for functions of type Result that contain expect() or unwrap()

Why restrict this?

These functions promote recoverable errors to non-recoverable errors, which may be undesirable in code bases which wish to avoid panics, or be a bug in the specific function.

Known problems

This can cause false positives in functions that handle both recoverable and non recoverable errors.

Example

Before:

fn divisible_by_3(i_str: String) -> Result<(), String> {
    let i = i_str
        .parse::<i32>()
        .expect("cannot divide the input by three");

    if i % 3 != 0 {
        Err("Number is not divisible by 3")?
    }

    Ok(())
}

After:

fn divisible_by_3(i_str: String) -> Result<(), String> {
    let i = i_str
        .parse::<i32>()
        .map_err(|e| format!("cannot divide the input by three: {}", e))?;

    if i % 3 != 0 {
        Err("Number is not divisible by 3")?
    }

    Ok(())
}
Applicability: Unspecified(?)
Added in: 1.48.0

What it does

Checks for .unwrap() or .unwrap_err() calls on Results and .unwrap() call on Options.

Why restrict this?

It is better to handle the None or Err case, or at least call .expect(_) with a more helpful message. Still, for a lot of quick-and-dirty code, unwrap is a good choice, which is why this lint is Allow by default.

result.unwrap() will let the thread panic on Err values. Normally, you want to implement more sophisticated error handling, and propagate errors upwards with ? operator.

Even if you want to panic on errors, not all Errors implement good messages on display. Therefore, it may be beneficial to look at the places where they may get displayed. Activate this lint to do just that.

Examples

option.unwrap();
result.unwrap();

Use instead:

option.expect("more helpful message");
result.expect("more helpful message");

If expect_used is enabled, instead:

option?;

// or

result?;

Past names

  • option_unwrap_used
  • result_unwrap_used

Configuration

  • allow-unwrap-in-consts: Whether unwrap should be allowed in code always evaluated at compile time

    (default: true)

  • allow-unwrap-in-tests: Whether unwrap should be allowed in test functions or #[cfg(test)]

    (default: false)

Applicability: Unspecified(?)
Added in: 1.45.0

What it does

Checks for usage of Debug formatting. The purpose of this lint is to catch debugging remnants.

Why restrict this?

The purpose of the Debug trait is to facilitate debugging Rust code, and no guarantees are made about its output. It should not be used in user-facing output.

Example

println!("{:?}", foo);
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for unnecessary repetition of structure name when a replacement with Self is applicable.

Why is this bad?

Unnecessary repetition. Mixed use of Self and struct name feels inconsistent.

Known problems

  • Unaddressed false negative in fn bodies of trait implementations

Example

struct Foo;
impl Foo {
    fn new() -> Foo {
        Foo {}
    }
}

could be

struct Foo;
impl Foo {
    fn new() -> Self {
        Self {}
    }
}

Configuration

  • msrv: The minimum rust version that the project supports. Defaults to the rust-version field in Cargo.toml

    (default: current version)

Applicability: MachineApplicable(?)
Added in: pre 1.29.0

What it does

Checks for the use of bindings with a single leading underscore.

Why is this bad?

A single leading underscore is usually used to indicate that a binding will not be used. Using such a binding breaks this expectation.

Known problems

The lint does not work properly with desugaring and macro, it has been allowed in the meantime.

Example

let _x = 0;
let y = _x + 1; // Here we are using `_x`, even though it has a leading
                // underscore. We should rename `_x` to `x`
Applicability: Unspecified(?)
Added in: pre 1.29.0

What it does

Checks for the use of item with a single leading underscore.

Why is this bad?

A single leading underscore is usually used to indicate that a item will not be used. Using such a item breaks this expectation.

Example

fn _foo() {}

struct _FooStruct {}

fn main() {
    _foo();
    let _ = _FooStruct{};
}

Use instead:

fn foo() {}

struct FooStruct {}

fn main() {
    foo();
    let _ = FooStruct{};
}
Applicability: Unspecified(?)
Added in: 1.83.0

What it does

Checks for variable declarations immediately followed by a conditional affectation.

Why is this bad?

This is not idiomatic Rust.

Example

let foo;

if bar() {
    foo = 42;
} else {
    foo = 0;
}

let mut baz = None;

if bar() {
    baz = Some(42);
}

should be written

let foo = if bar() {
    42
} else {
    0
};

let baz = if bar() {
    Some(42)
} else {
    None
};
Applicability: HasPlaceholders(?)
Added in: pre 1.29.0

What it does

Checks for bit masks that can be replaced by a call to trailing_zeros

Why is this bad?

x.trailing_zeros() >= 4 is much clearer than x & 15 == 0

Example

if x & 0b1111 == 0 { }

Use instead:

if x.trailing_zeros() >= 4 { }

Configuration

  • verbose-bit-mask-threshold: The maximum allowed size of a bit mask before suggesting to use ‘trailing_zeros’

    (default: 1)

Applicability: MaybeIncorrect(?)
Added in: pre 1.29.0

What it does

Checks for usage of File::read_to_end and File::read_to_string.

Why restrict this?

fs::{read, read_to_string} provide the same functionality when buf is empty with fewer imports and no intermediate values. See also: fs::read docs, fs::read_to_string docs

Example

let mut f = File::open("foo.txt").unwrap();
let mut bytes = Vec::new();
f.read_to_end(&mut bytes).unwrap();

Can be written more concisely as

let mut bytes = fs::read("foo.txt").unwrap();
Applicability: Unspecified(?)
Added in: 1.44.0

What it does

Checks for while loops comparing floating point values.

Why is this bad?

If you increment floating point values, errors can compound, so, use integers instead if possible.

Known problems

The lint will catch all while loops comparing floating point values without regarding the increment.

Example

let mut x = 0.0;
while x < 42.0 {
    x += 1.0;
}

Use instead:

let mut x = 0;
while x < 42 {
    x += 1;
}
Applicability: Unspecified(?)
Added in: 1.80.0

What it does

Checks for wildcard dependencies in the Cargo.toml.

Why is this bad?

As the edition guide says, it is highly unlikely that you work with any possible version of your dependency, and wildcard dependencies would cause unnecessary breakage in the ecosystem.

Example

[dependencies]
regex = "*"

Use instead:

[dependencies]
some_crate_1 = "~1.2.3"

some_crate_2 = "=1.2.3"
Applicability: Unspecified(?)
Added in: 1.32.0

What it does

Checks for wildcard enum matches using _.

Why restrict this?

New enum variants added by library updates can be missed.

Known problems

Suggested replacements may be incorrect if guards exhaustively cover some variants, and also may not use correct path to enum if it’s not present in the current scope.

Example

match x {
    Foo::A(_) => {},
    _ => {},
}

Use instead:

match x {
    Foo::A(_) => {},
    Foo::B(_) => {},
}
Applicability: MaybeIncorrect(?)
Added in: 1.34.0

What it does

Checks for wildcard imports use _::*.

Why is this bad?

wildcard imports can pollute the namespace. This is especially bad if you try to import something through a wildcard, that already has been imported by name from a different source:

use crate1::foo; // Imports a function named foo
use crate2::*; // Has a function named foo

foo(); // Calls crate1::foo

This can lead to confusing error messages at best and to unexpected behavior at worst.

Exceptions

Wildcard imports are allowed from modules that their name contains prelude. Many crates (including the standard library) provide modules named “prelude” specifically designed for wildcard import.

Wildcard imports reexported through pub use are also allowed.

use super::* is allowed in test modules. This is defined as any module with “test” in the name.

These exceptions can be disabled using the warn-on-all-wildcard-imports configuration flag.

Known problems

If macros are imported through the wildcard, this macro is not included by the suggestion and has to be added by hand.

Applying the suggestion when explicit imports of the things imported with a glob import exist, may result in unused_imports warnings.

Example

use crate1::*;

foo();

Use instead:

use crate1::foo;

foo();

Configuration

  • allowed-wildcard-imports: List of path segments allowed to have wildcard imports.

Example

allowed-wildcard-imports = [ "utils", "common" ]

Noteworthy

  1. This configuration has no effects if used with warn_on_all_wildcard_imports = true.
  2. Paths with any segment that containing the word ‘prelude’ are already allowed by default.

(default: [])

  • warn-on-all-wildcard-imports: Whether to emit warnings on all wildcard imports, including those from prelude, from super in tests, or for pub use reexports.

    (default: false)

Applicability: MachineApplicable(?)
Added in: 1.43.0

What it does

Checks for maps with zero-sized value types anywhere in the code.

Why is this bad?

Since there is only a single value for a zero-sized type, a map containing zero sized values is effectively a set. Using a set in that case improves readability and communicates intent more clearly.

Known problems

  • A zero-sized type cannot be recovered later if it contains private fields.
  • This lints the signature of public items

Example

fn unique_words(text: &str) -> HashMap<&str, ()> {
    todo!();
}

Use instead:

fn unique_words(text: &str) -> HashSet<&str> {
    todo!();
}
Applicability: Unspecified(?)
Added in: 1.50.0