Skip to content

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

@scottmcm

Description

@scottmcm

What it does

Inspired by the bug report in rust-lang/rust#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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintArea: New lints

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions