Skip to content

needless_join for Iter<String> #8570

Closed
@yoav-lavi

Description

@yoav-lavi

What it does

When transforming an Iter<String> to String, you can either use:

.collect::<Vec<String>>().join(""):

let vector = vec!["hello",  "world"];
let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
println!("{}", output);

or

.collect::<String>():

let vector = vec!["hello",  "world"];
let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
println!("{}", output);

The latter being terser and more performant:

fn criterion_benchmark(criterion: &mut Criterion) {
    criterion.bench_function("1", |bencher| {
        bencher.iter(|| {
            vec!["hello", "world"]
                .iter()
                .map(|item| item.to_uppercase())
                .collect::<Vec<String>>()
                .join("")
        })
    });
    criterion.bench_function("2", |bencher| {
        bencher.iter(|| {
            vec!["hello", "world"]
                .iter()
                .map(|item| item.to_uppercase())
                .collect::<String>()
        })
    });
}
1                       time:   [155.79 ns 155.97 ns 156.16 ns]                   
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) high mild
  2 (2.00%) high severe
slope  [155.79 ns 156.16 ns] R^2            [0.9971885 0.9971738]
mean   [155.87 ns 156.58 ns] std. dev.      [672.36 ps 2.9345 ns]
median [156.12 ns 156.26 ns] med. abs. dev. [209.02 ps 650.40 ps]

2                       time:   [118.11 ns 118.20 ns 118.28 ns]                   
Found 8 outliers among 100 measurements (8.00%)
  8 (8.00%) high severe
slope  [118.11 ns 118.28 ns] R^2            [0.9987699 0.9987697]
mean   [118.31 ns 118.87 ns] std. dev.      [794.06 ps 2.0461 ns]
median [117.96 ns 118.43 ns] med. abs. dev. [154.11 ps 692.31 ps]

I suggest adding a new lint called needless_join that detects the case of .collect<Vec<String>>.join("") on a String iterator.

Thank you for your consideration!

Lint Name

needless_join

Category

perf

Advantage

  • It is shorter
  • It is more performant
  • The intent is clearer

Drawbacks

None that I'm aware of

Example

let vector = vec!["hello",  "world"];
let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
println!("{}", output);

Could be written as:

let vector = vec!["hello",  "world"];
let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
println!("{}", output);

See cases of this happening in the wild here (GitHub CS Beta) or here (Sourcegraph)

I'm learning Rust so please let me know if I'm missing something!

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