Rust: `Option::filter` doesn't act as expected

Created on 11 Sep 2020  路  4Comments  路  Source: rust-lang/rust

I tried this code, which is also available in Rust Playground:

let u: Vec<Option<u8>> = vec![Some(0), Some(2), None];
let v: Vec<u8> = u.iter().filter(|_|true).map(|x|x.unwrap()).collect();
println!("{:?}", v);

I expect to get vec![0, 2], because the documentations says,

Returns None if the option is None, otherwise calls predicate with the wrapped value and returns:

  • Some(t) if predicate returns true (where t is the wrapped value), and
  • None if predicate returns false.
    so any None should be dropped, regardless of the predicate.

Instead, it panics for unwrapping a None value. So obviously this differs from the documentation.

Meta

rustc --version --verbose:

1.46.0

Backtrace

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:3:56
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.46/src/backtrace/libunwind.rs:86
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.46/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:78
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:59
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1076
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1537
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:62
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:198
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:217
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:526
  11: rust_begin_unwind
             at src/libstd/panicking.rs:437
  12: core::panicking::panic_fmt
             at src/libcore/panicking.rs:85
  13: core::panicking::panic
             at src/libcore/panicking.rs:50
  14: core::option::Option<T>::unwrap
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/libcore/macros/mod.rs:10
  15: playground::main::{{closure}}
             at src/main.rs:3
  16: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &mut F>::call_once
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/libcore/ops/function.rs:286
  17: core::option::Option<T>::map
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/libcore/option.rs:453
  18: <core::iter::adapters::Map<I,F> as core::iter::traits::iterator::Iterator>::next
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/libcore/iter/adapters/mod.rs:852
  19: alloc::vec::Vec<T>::extend_desugared
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/liballoc/vec.rs:2217
  20: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T,I>>::spec_extend
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/liballoc/vec.rs:2110
  21: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T,I>>::from_iter
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/liballoc/vec.rs:2105
  22: <alloc::vec::Vec<T> as core::iter::traits::collect::FromIterator<T>>::from_iter
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/liballoc/vec.rs:1995
  23: core::iter::traits::iterator::Iterator::collect
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/libcore/iter/traits/iterator.rs:1671
  24: playground::main
             at src/main.rs:3
  25: std::rt::lang_start::{{closure}}
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/libstd/rt.rs:67
  26: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:52
  27: std::panicking::try::do_call
             at src/libstd/panicking.rs:348
  28: std::panicking::try
             at src/libstd/panicking.rs:325
  29: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  30: std::rt::lang_start_internal
             at src/libstd/rt.rs:51
  31: std::rt::lang_start
             at /rustc/04488afe34512aa4c33566eb16d8c912a3ae04f9/src/libstd/rt.rs:67
  32: main
  33: __libc_start_main
  34: _start
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

C-bug

Most helpful comment

You are calling Iterator::filter, not Option::filter

All 4 comments

You're not calling Option::filter but Filter::filter that comes from Filter iterator.

u.iter() // returns iterator over all Options in the Vec
   .filter(|_|true) // closure returns true for every option in the vec, so you get back the same iterator
   .map(|x| x.unwrap()) // program panics on the last Option, because it wasn't filtered out
   .collect()

You probably want something like

u.iter() 
   .filter(|x| x.is_some()) 
   .map(|x| x.unwrap()) 
   .collect()

You are calling Iterator::filter, not Option::filter

ah yeah thanks

Also, you can use filter_map, it is meant for exactly this use case:

let v: Vec<u8> = u.iter().filter_map(|&x| x ).collect();

In this specific case, you can also use the consuming one so you don't need the dereference

let v: Vec<u8> = u.into_iter().filter_map(|x| x ).collect();

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nikomatsakis picture nikomatsakis  路  412Comments

thestinger picture thestinger  路  234Comments

Leo1003 picture Leo1003  路  898Comments

nikomatsakis picture nikomatsakis  路  331Comments

withoutboats picture withoutboats  路  308Comments