Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

while_let_on_iterator should not trigger on borrowed iterators if the iterator is subsequently used. #7659

Open
peterjoel opened this issue Sep 10, 2021 · 12 comments
Labels
C-bug Category: Clippy is not doing the correct thing I-false-positive Issue: The lint was triggered on code it shouldn't have

Comments

@peterjoel
Copy link

Lint name: while_let_on_iterator

I tried this code:

fn parse_value<V>(tokens: TokenStream) -> parse::Result<(ValueExpr<V>, TokenStream)>
where
    ValueExpr<V>: Parse,
{
    let mut it = tokens.into_iter();
    let mut value = TokenStream::new();
    while let Some(tt) = it.next() {
        if let TokenTree::Punct(p) = &tt {
            if p.as_char() == ',' {
                break;
            }
        }
        value.append(tt);
    }
    Ok((ValueExpr::parse.parse2(value)?, it.collect()))
}

I expected to see this happen:

This should not trigger the lint because the iterator is used again after the loop. The purpose of explicitly calling next() is to make it clear that the iterator may not be fully consumed in the loop and the remaining items can be processed elsewhere.

Instead, this happened: The lint triggers and tells me to use a for loop.

Meta

Rust version (rustc -Vv):

rustc 1.55.0 (c8dfcfe04 2021-09-06)
binary: rustc
commit-hash: c8dfcfe046a7680554bf4eb612bad840e7631c4b
commit-date: 2021-09-06
host: x86_64-apple-darwin
release: 1.55.0
LLVM version: 12.0.1
@peterjoel peterjoel added C-bug Category: Clippy is not doing the correct thing I-false-positive Issue: The lint was triggered on code it shouldn't have labels Sep 10, 2021
@rustbot

This comment has been minimized.

@camsteffen

This comment has been minimized.

@xFrednet

This comment has been minimized.

@Jarcho
Copy link
Contributor

Jarcho commented Sep 15, 2021

It's hard to call this one a false positive as the suggestion is correct in that you can use a for loop there. You have a subjective point about why not to use one which I personally disagree with. I would assume you were using the iterator within the loop, not necessarily afterwards. It might be a good idea to split the into two so you could disable just part of it.

As an aside I would be writing the code more like this (if you like combinators):

let mut it = tokens.into_iter();
let value: TokenStream = (&mut it).take_while(|x| match x {
    TokenTree::Punct(p) => p.as_char() != ',',
    _ => true,
}).collect();
Ok((ValueExpr::parse.parse2(value)?, it.collect()))

@Jarcho
Copy link
Contributor

Jarcho commented Sep 15, 2021

Looks like clippy is correct there. You can use for mut ptr in &mut iter there.

@jjl
Copy link

jjl commented Sep 15, 2021

yeah, i deleted my comment when i realised (some half an hour later...).

i'm not sure if the lint suggestion is more obvious from the cli, but it really wasn't through lsp-mode

@peterjoel
Copy link
Author

peterjoel commented Sep 16, 2021

@jackh726 My point about it being better style not to use a for loop is definitely debatable, but I agree that it is much more expressive to use take_while here. It can be further improved with matches!:

let value: TokenStream = (&mut it)
    .take_while(|x| !matches!(x, TokenTree::Punct(p) if  p.as_char() == ','))
    .collect();

@ghost
Copy link

ghost commented Sep 16, 2021

I think by_ref can be used as an alternative to &mut it.

let value: TokenStream = it.by_ref()
    .take_while(|x| !matches!(x, TokenTree::Punct(p) if  p.as_char() == ','))
    .collect();

@peterjoel
Copy link
Author

@mikerite There should be a clippy lint for this!

@jjl
Copy link

jjl commented Sep 16, 2021

If clippy were to have suggested by_ref, i think i would have understood much more quickly.

@Jarcho
Copy link
Contributor

Jarcho commented Sep 17, 2021

It could definitely change to use by_ref if people prefer that. I have no opinion either way here; I think they both look fine.

bors added a commit that referenced this issue Sep 19, 2021
Change `while_let_on_iterator` suggestion to use `by_ref()`

It came up in the discussion #7659 that suggesting `iter.by_ref()` is a clearer suggestion than `&mut iter`. I personally think they're equivalent, but if `by_ref()` is clearer to people then that should be the suggestion.

changelog: Change `while_let_on_iterator` suggestion when using `&mut` to use `by_ref()`
@Jarcho
Copy link
Contributor

Jarcho commented Apr 6, 2022

I think this can be closed now. iter.by_ref() is now suggested over &mut iter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Clippy is not doing the correct thing I-false-positive Issue: The lint was triggered on code it shouldn't have
Projects
None yet
Development

No branches or pull requests

6 participants