rust-lang / rust-lang/rust

rustc shouldn't suggest returning `Option<&Option<T>>` in `filter_map`

Open
#122,746 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

A-diagnostics T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code
struct NonCopy;

fn main() {
    _ = vec![Some(()), None, Some(())].iter().filter_map(|x| x);
    _ = vec![Some(NonCopy), None, Some(NonCopy)].iter().filter_map(|x| x);
    _ = vec![Some(()), None, Some(())].iter_mut().filter_map(|x| x);
    _ = vec![Some(NonCopy), None, Some(NonCopy)].iter_mut().filter_map(|x| x);
}
Current output
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:4:62
  |
4 |     _ = vec![Some(()), None, Some(())].iter().filter_map(|x| x);
  |                                                              ^ expected `Option<_>`, found `&Option<()>`
  |
  = note:   expected enum `Option<_>`
          found reference `&Option<()>`
help: try wrapping the expression in `Some`
  |
4 |     _ = vec![Some(()), None, Some(())].iter().filter_map(|x| Some(x));
  |                                                              +++++ +

error[E0308]: mismatched types
 --> src/main.rs:5:72
  |
5 |     _ = vec![Some(NonCopy), None, Some(NonCopy)].iter().filter_map(|x| x);
  |                                                                        ^ expected `Option<_>`, found `&Option<NonCopy>`
  |
  = note:   expected enum `Option<_>`
          found reference `&Option<NonCopy>`
help: try wrapping the expression in `Some`
  |
5 |     _ = vec![Some(NonCopy), None, Some(NonCopy)].iter().filter_map(|x| Some(x));
  |                                                                        +++++ +

error[E0308]: mismatched types
 --> src/main.rs:6:66
  |
6 |     _ = vec![Some(()), None, Some(())].iter_mut().filter_map(|x| x);
  |                                                                  ^ expected `Option<_>`, found `&mut Option<()>`
  |
  = note:           expected enum `Option<_>`
          found mutable reference `&mut Option<()>`
help: try wrapping the expression in `Some`
  |
6 |     _ = vec![Some(()), None, Some(())].iter_mut().filter_map(|x| Some(x));
  |                                                                  +++++ +

error[E0308]: mismatched types
 --> src/main.rs:7:76
  |
7 |     _ = vec![Some(NonCopy), None, Some(NonCopy)].iter_mut().filter_map(|x| x);
  |                                                                            ^ expected `Option<_>`, found `&mut Option<NonCopy>`
  |
  = note:           expected enum `Option<_>`
          found mutable reference `&mut Option<NonCopy>`
help: try wrapping the expression in `Some`
  |
7 |     _ = vec![Some(NonCopy), None, Some(NonCopy)].iter_mut().filter_map(|x| Some(x));
  |                                                                            +++++ +

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 4 previous errors
Desired output
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:4:62
  |
4 |     _ = vec![Some(()), None, Some(())].iter().filter_map(|x| x);
  |                                                              ^ expected `Option<_>`, found `&Option<()>`
  |
  = note:   expected enum `Option<_>`
          found reference `&Option<()>`
help: try dereferencing `x`
  |
4 |     _ = vec![Some(()), None, Some(())].iter().filter_map(|&x| x); // or *x
  |                                                           +

error[E0308]: mismatched types
 --> src/main.rs:5:72
  |
5 |     _ = vec![Some(NonCopy), None, Some(NonCopy)].iter().filter_map(|x| x);
  |                                                                        ^ expected `Option<_>`, found `&Option<NonCopy>`
  |
  = note:   expected enum `Option<_>`
          found reference `&Option<NonCopy>`
help: try calling `.as_ref()` on `x`
  |
5 |     _ = vec![Some(NonCopy), None, Some(NonCopy)].iter().filter_map(|x| x.as_ref());
  |                                                                         +++++++++

error[E0308]: mismatched types
 --> src/main.rs:6:66
  |
6 |     _ = vec![Some(()), None, Some(())].iter_mut().filter_map(|x| x);
  |                                                                  ^ expected `Option<_>`, found `&mut Option<()>`
  |
  = note:           expected enum `Option<_>`
          found mutable reference `&mut Option<()>`
help: try dereferencing `x`
  |
6 |     _ = vec![Some(()), None, Some(())].iter_mut().filter_map(|&mut x| x); // or *x
  |                                                               ++++

error[E0308]: mismatched types
 --> src/main.rs:7:76
  |
7 |     _ = vec![Some(NonCopy), None, Some(NonCopy)].iter_mut().filter_map(|x| x);
  |                                                                            ^ expected `Option<_>`, found `&mut Option<NonCopy>`
  |
  = note:           expected enum `Option<_>`
          found mutable reference `&mut Option<NonCopy>`
help: try calling `.as_mut()` on `x`
  |
7 |     _ = vec![Some(NonCopy), None, Some(NonCopy)].iter_mut().filter_map(|x| x.as_mut());
  |                                                                             +++++++++

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 4 previous errors
Rationale and extra context

Some(x) is useless in filter_map, and is equivalent to map(|x| x) (which is equivalent to no mapping at all). We shouldn't suggest this at all really, but it's good enough for the general case - but with &Option<T>, we should really be able to tell they want to filter by this option instead.

This could be caused by changing into_iter to iter, as you then have &Option<T>. The user could write this using flatten instead but they're not guided in that (correct) direction - if this change is made, clippy will then suggest flatten (rust-lang/rust-clippy#12501)

Other cases

No response

Rust Version
rustc 1.78.0-nightly (766bdce74 2024-03-16)
binary: rustc
commit-hash: 766bdce744d531267d53ba2a3f9ffcda69fb9b17
commit-date: 2024-03-16
host: x86_64-pc-windows-msvc
release: 1.78.0-nightly
LLVM version: 18.1.2
Anything else?

We could limit this to only |x| x instead of something like |(x, y)| if true { x } else { y }. Both of these should work, although the second case isn't relevant to flatten, but it should still make sense?

Considering how general this error message is (suggest_compatible_variants), I think we could probably generalize this further, instead of only suggesting this in filter_map.

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

Reproduce the examples with rustc and start by tracing the general suggest_compatible_variants diagnostic path, especially its handling of filter_map closures and references. Done means the four cases suggest dereferencing, .as_ref(), or .as_mut() instead of wrapping the reference in Some, while preserving the existing general behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.