rust-lang / rust-lang/rust-clippy

On slices and vectors, encourage slice operations instead of iterator methods like `nth` or `skip`

Open
#16,441 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

What it does

Inspired by the bug report in https://github.com/rust-lang/rust/issues/150235, tell people to just use slices when they make transient iterators to do things that are easier to just stay on slices.

For example, that issue is doing (where s.stack is a Vec)

    let mut iter = s.stack.iter_mut();
    let parent = iter.nth(s.depth - 1).unwrap();
    let current = iter.next().unwrap();

then never using the iterator again.

It would be simpler to never make the iterator in the first place, and use different slice methods instead.

For example, it could use

let (parent, current) = s.stack.get_disjoint_mut([s.depth - 1, s.depth]).unwrap();

or because they're adjacent it could use one of

let [parent, current] = s.stack[s.depth - 1..].first_chunk_mut().unwrap();
let [parent, current] = s.stack[..=s.depth].last_chunk_mut().unwrap();
Advantage
  • Less complicated code, because you as the human don't need to think about the state of the mutable iterator
  • Easier for the compiler to optimize, because the slice methods can directly check against the length
Drawbacks

The slice code only works on slices (or things that deref to slices) so it would make it harder to change the Vec<T> into SomeFancyNonContiguousContainer<T> later. (That said, you can still easily move to ThinVec<T> or SmallVec<T> or ... since they all still deref to slices.)

This can probably get arbitrarily-complicated to lint in clippy.

Example

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=17b74bf951650c9a1ca5e8d36bac8a02

pub fn foo(x: &mut [String]) {
    let mut it = x.iter_mut().skip(2);
    let a = it.next().unwrap();
    let b = it.next().unwrap();
    do_something_with((a, b));
}

Could be written as:

pub fn foo(x: &mut [String]) {
    let [a, b] = x[2..].first_chunk_mut::<2>().unwrap();
    do_something_with((a, b));
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=ed67da77f04948f2270ab82520271e1a

Comparison with existing lints

This doesn't lint today.

It's similar to clippy::iter_nth, but is about going further to look at more complex situations and other possible methods.

Additional Context

This is a large bucket of possible things, far more than just the examples listed.

In some sense, it's possible that anything making a slice iterator then not actually looping nor using any of the looping methods (nor passing it to something that needs an iterator) could want to be re-written to avoid the iterator.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the existing clippy::iter_nth lint and the slice-operation examples in this issue. Define which transient iterator patterns are in scope, how the lint should handle the listed alternatives and limitations, and what cases must be covered before the work is considered complete.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.