Anyone have real-world examples of using Option as an iterator?


#1

I’m writing an article on using Option as an iterator, but I’m not sure what to write for the examples. All the use cases I came up with are better served by if let or std::iter::once.

Does anyone have any short, realistic snippets of code that use Option as an iterator?


#2

@lfairy: Generally speaking, there aren’t many great reasons, but I’ve used this in aster. There, I use it to short circuit adding things to vecs:

    fn build_(mut self, expr: Option<P<ast::Expr>>) -> F::Result {
                self.stmts.extend(expr.map(|expr| {
                    StmtBuilder::new().span(expr.span).build_expr(expr)
                }));
                self.callback.invoke(P(ast::Block {
                    stmts: self.stmts,
                    id: ast::DUMMY_NODE_ID,
                    rules: self.block_check_mode,
                    span: self.span,
                }))
            }

#3

I was recently wrapping a C API that returned an index as a string. It used a one-based index with zero meaning there was no current index. I wanted to convert zero to None and change the index to zero-based. So I converted an Option to an Iterator and called filter:

s.parse::<usize>().ok().into_iter().filter(|i| *i != 0).next().map(|i| i - 1)

BTW, this is optimized to assembly that I couldn’t write better by hand (one call to parse(), then three comparisons and jumps).


#4

I think this is something I “take advantage of” every once in a while, but never use intentionally. I’ll pass an Option to something that takes an IntoIterator, or I’ll flat_map over an Iterator of Options, and so on.


#5

Surely filter_map is better for that? I guess that’s just you taking advantage without intention…


#6

rustc uses it a bunch. The situation is basically that you have collections of some items (As Vec or so), and then you have extra single items.

Example:

/// A path like `Foo(A,B) -> C`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct ParenthesizedParameterData {
    /// Overall span
    pub span: Span,

    /// `(A,B)`
    pub inputs: HirVec<P<Ty>>,

    /// `C`
    pub output: Option<P<Ty>>,
}

The vec (data.inputs) and the option (data.output) are iterated here:

    pub fn types(&self) -> HirVec<&P<Ty>> {
        match *self {
            AngleBracketedParameters(ref data) => {
                data.types.iter().collect()
            }
            ParenthesizedParameters(ref data) => {
                data.inputs
                    .iter()
                    .chain(data.output.iter())
                    .collect()
            }
        }
    }

rustc revealed to me that the uniformity that comes from treating Option as a 0 or 1 element collection is pretty neat.


#7

I commonly use it to add a single element to an iterator with chain: https://github.com/steffengy/schannel-rs/blob/master/src/tls_stream.rs#L21-L22

Or to feed a single item to an API that takes an iterator over values: https://github.com/sfackler/rust-postgres/blob/master/src/types/mod.rs#L593-L598

I can’t think of a time when I’ve used None as an iterable though.


#8

I feel like when I’m passing the identity function, flat_map is clearer than filter_map, but ymmv.