# Replacing combinator functions with language features This document shows all the `Option` and `Result` combinators that could potentially be replaced by language features. Each code block shows the description and signature of a combinator function, followed by one or more Rust expressions showing how to replace it using `is` or `try`. Some of these combinators are still potentially useful in other contexts, as well as in combination with other things that don't have equivalents; an example replacement for a given combinator does not mean I'm advocating removing the combinator or suggesting that every use of the combinator can always be replaced. ## `Option` ```rust /// Returns `true` if the option is a [`Some`] value. pub const fn is_some(&self) -> bool; if opt is Some(_) {} ``` ```rust /// Returns `true` if the option is a [`Some`] and the value inside of it matches a predicate. pub fn is_some_and(&self, f: impl FnOnce(&T) -> bool) -> bool; if opt is Some(x) && expr(x) {} // Handles both by-ref and by-value if opt is Some(ref x) && expr(x) {} if &opt is Some(x) && expr(x) {} ``` ```rust /// Returns `true` if the option is a [`None`] value. pub const fn is_none(&self) -> bool; if opt is None {} ``` ```rust /// Converts from `&Option<T>` to `Option<&T>`. pub const fn as_ref(&self) -> Option<&T>; if opt is Some(ref x) {} if &opt is Some(x) {} // If you already have a reference if opt is Some(x) {} ``` ```rust /// Converts from `&mut Option<T>` to `Option<&mut T>`. pub const fn as_mut(&mut self) -> Option<&mut T>; if opt is Some(ref mut x) {} ``` ```rust /// Maps an `Option<T>` to `Option<U>` by applying a function to a contained value. pub const fn map<U, F>(self, f: F) -> Option<U>; try { f(x?) } ``` ```rust /// Calls the provided closure with a reference to the contained value (if [`Some`]). pub const fn inspect<F>(self, f: F) -> Self; // If the return value doesn't matter: try { f(x?); } ``` ```rust /// Zips `self` and another `Option` with function `f`. pub const fn zip_with<U, F, R>(self, other: Option<U>, f: F) -> Option<R>; try { f(opt_a?, opt_b?) } ``` ```rust /// Returns [`None`] if the option is [`None`], otherwise returns `optb`. pub const fn and<U>(self, optb: Option<U>) -> Option<U>; try { opt_a?; opt_b? } ``` ```rust /// Returns [`None`] if the option is [`None`], otherwise calls `f` with the pub const fn and_then<U, F>(self, f: F) -> Option<U>; try { f(opt_a?)? } ``` ```rust /// Returns `true` if the option is a [`Some`] value containing the given value. pub const fn contains<U>(&self, x: &U) -> bool; if opt is Some(v) && v == x {} ``` ```rust /// Zips `self` with another `Option`. pub const fn zip<U>(self, other: Option<U>) -> Option<(T, U)>; try { (opt_a?, opt_b?) } ``` ```rust /// Zips `self` and another `Option` with function `f`. pub const fn zip_with<U, F, R>(self, other: Option<U>, f: F) -> Option<R>; try { f(opt_a?, opt_b?) } ``` ```rust /// Maps an `Option<&T>` to an `Option<T>` by copying the contents of the pub const fn copied(self) -> Option<T>; opt is Some(&v) try { *opt? } ``` ```rust /// Maps an `Option<&T>` to an `Option<T>` by cloning the contents of the pub const fn cloned(self) -> Option<T>; try { opt?.clone() } ``` ```rust /// Converts from `Option<Option<T>>` to `Option<T>`. pub const fn flatten(self) -> Option<T>; try { opt?? } ``` ## `Result` ```rust /// Returns `true` if the result is [`Ok`]. pub const fn is_ok(&self) -> bool; if x is Ok(_) {} ``` ```rust /// Returns `true` if the result is [`Ok`] and the value inside of it matches a predicate. pub fn is_ok_and(&self, f: impl FnOnce(&T) -> bool) -> bool; if x is Ok(v) && expr(v) {} ``` ```rust /// Returns `true` if the result is [`Err`]. pub const fn is_err(&self) -> bool; if x is Err(_) {} ``` ```rust /// Returns `true` if the result is [`Err`] and the value inside of it matches a predicate. pub fn is_err_and(&self, f: impl FnOnce(&E) -> bool) -> bool; if x is Err(e) && expr(e) {} ``` The general case of `as_ref` and `as_mut` isn't replaceable, but any usage that only needs one variant or the other may be able to use `is`. ```rust /// Maps a `Result<T, E>` to `Result<U, E>` by applying a function to a pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Result<U, E>; try { f(x?) } ``` ```rust /// Calls the provided closure with a reference to the contained value (if [`Ok`]). pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self; // If the return value doesn't matter: try { f(x?); } ``` ```rust /// Returns `res` if the result is [`Ok`], otherwise returns the [`Err`] value of `self`. pub const fn and<U>(self, res: Result<U, E>) -> Result<U, E>; try { x?; y? } ``` ```rust /// Calls `op` if the result is [`Ok`], otherwise returns the [`Err`] value of `self`. pub fn and_then<U, F: FnOnce(T) -> Result<U, E>>(self, op: F) -> Result<U, E>; try { f(x?)? } ``` ```rust /// Returns `true` if the result is an [`Ok`] value containing the given value. pub fn contains<U>(&self, x: &U) -> bool; if res is Ok(v) && v == x ``` ```rust /// Maps a `Result<&T, E>` to a `Result<T, E>` by copying the contents of the pub fn copied(self) -> Result<T, E>; try { *x? } ``` ```rust /// Maps a `Result<&T, E>` to a `Result<T, E>` by cloning the contents of the pub fn cloned(self) -> Result<T, E>; try { x?.clone() } ``` ```rust /// Converts from `Result<Result<T, E>, E>` to `Result<T, E>` pub fn flatten(self) -> Result<T, E>; try { x?? } ``` ## Future possibilities: `as_deref` and `as_deref_mut` might be possible with deref patterns. The various `or` and `unwrap_or` and `map_or` and similar functions may be possible with some kind of coalescing operation. I'd prioritize that below `is` and `try` though.