rust-lang / rust-lang/rust-clippy

New lint: Suggest `zip_eq` over `zip` or explicitly opt out

Open
#7,430 1 comment 1 reaction 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

When a user uses the .zip() method on Iterator, it will "silently fail" if the two iterators are of unequal length. A majority of the time, (my hunch is that) users intend to .zip() iterators of equal length, and when they aren't, it may cause silent failures in other parts of the program.

Categories (optional)
  • Kind: clippy::suspicious, maybe clippy::pedantic (and implicitly clippy::nursery)
Advantages
  • Forces the user to explicitly annotate when they want the behavior of zip
  • Makes silent failures due to unequal zip lengths far less common
Drawbacks
  • Recommends a third party library, itertools, to get zip_eq (perhaps this could be put into the standard library in the future)
  • Mildlly annoying if you do want the behavior of zip, of which you would then want an explicit annotation
Example
fn main() {
    let users = vec!["John", "Mary", "Kevin"];
    let balances = vec![10.0, 20.0];

    println!("These are the users in our system:");
    for (name, balance) in users.iter().zip(balances.iter()) {
        println!("{} - ${}", name, balance);
    }
}

Could be written as:

use itertools::Itertools;

fn main() {
    let users = vec!["John", "Mary", "Kevin"];
    let balances = vec![10.0, 20.0];

    println!("These are the users in our system:");
    for (name, balance) in users.iter().zip_eq(balances.iter()) {
        println!("{} - ${}", name, balance);
    }
}

Or, if you want zip's behavior,

fn main() {
    let users = vec!["John", "Mary", "Kevin"];
    let balances = vec![10.0, 20.0];

    println!("These are the users in our system:");
    #[allow(prefer_zip)] // specific lint name requires bikeshedding
    for (name, balance) in users.iter().zip(balances.iter()) {
        println!("{} - ${}", name, balance);
    }
}
Reasoning

On a project I'm working on, the fact that zip silently failed caused me to get mildly annoyed enough to force ensuring that the iterators are the same length a contributing guideline/codestyle, and I figured that I could suggest a lint over here to possibly do something about it. I'm sure the standard library folks had their reasoning for making zip behave the way it does, but I would say that a majority of the time, the two iterators are intended to be the same length. Having the lengths be unequal is similar to silent failure, where different parts of the system go broken, silently. Hopefully the appeal of such a lint is clear.

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 issue's examples and its proposed zip_eq and opt-out behavior, then determine the appropriate lint name and category. Done would mean an agreed design for detecting Iterator::zip uses and handling intentional truncation, but the issue does not name implementation files or tests.

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
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.