Option<T> and Result<T, E> in Depth
Iterating Over Optional Values
Both Option<T> and Result<T, E> implement IntoIterator, behaving as zero-or-one-element containers: Some(v) and Ok(v) yield 1 element, while None and Err(_) yield 0 elements.
Iterator Methods on Option<T> and Result<T, E>
| Method | Receiver | Item Type |
Yielded Items |
|---|---|---|---|
opt.iter() / res.iter() |
&self |
&T |
1 item if Some/Ok, 0 if None/Err |
opt.iter_mut() / res.iter_mut() |
&mut self |
&mut T |
1 item if Some/Ok, 0 if None/Err |
opt.into_iter() / res.into_iter() |
self |
T |
1 item if Some/Ok, 0 if None/Err |
opt.as_slice() |
&Option<T> |
&[T] |
Slice of length 1 or 0 |
Composability With .chain(), .extend(), and .flatten()
Because Option<T> implements IntoIterator<Item = T>, you can pass an Option<T> directly to any API that accepts impl IntoIterator<Item = T>—such as iter.chain(opt) or vec.extend(opt)—without branching on if let Some(x) = opt:
fn build_command_args(
binary: &str,
config_file: Option<&str>,
positional: &[&str],
) -> Vec<String> {
std::iter::once(binary)
.chain(config_file) // Appends 1 item if `Some`, or 0 items if `None`!
.chain(positional.iter().copied())
.map(String::from)
.collect()
}
fn main() {
let with_cfg = build_command_args("app", Some("--config=prod.toml"), &["serve"]);
let no_cfg = build_command_args("app", None, &["serve"]);
println!("With config: {with_cfg:?}");
println!("Without config: {no_cfg:?}");
}
With config: ["app", "--config=prod.toml", "serve"]
Without config: ["app", "serve"]
Extending a Collection from an Option<T>
Similarly, calling collection.extend(optional_value) appends the value if Some(x) and is a no-op if None:
fn main() {
let mut tags = vec!["rust", "2024"];
let extra_tag: Option<&str> = Some("cli");
let missing_tag: Option<&str> = None;
tags.extend(extra_tag);
tags.extend(missing_tag);
println!("Tags: {tags:?}");
}
Tags: ["rust", "2024", "cli"]