From e4fdfcae089b0b1b99f68f53221963e705aa1540 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 20 Oct 2024 21:41:30 +0200 Subject: [PATCH] chore: Documentation and unit tests for rosenpass_util::functional --- util/src/functional.rs | 175 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 1 deletion(-) diff --git a/util/src/functional.rs b/util/src/functional.rs index 183c903..fc2cb2b 100644 --- a/util/src/functional.rs +++ b/util/src/functional.rs @@ -1,4 +1,118 @@ -pub fn mutating(mut v: T, f: F) -> T +//! Syntax sugar & helpers for a functional programming style and method chains + +/// Mutate a value; mostly syntactic sugar +/// +/// # Examples +/// +/// ``` +/// use std::borrow::Borrow; +/// use rosenpass_util::functional::{mutating, MutatingExt, sideeffect, SideffectExt, ApplyExt}; +/// use rosenpass_util::mem::DiscardResultExt; +/// +/// // Say you have a function that takes a mutable reference +/// fn replace(slice: &mut [T], targ: T, by: T) { +/// for val in slice.iter_mut() { +/// if *val == targ { +/// *val = by; +/// } +/// } +/// } +/// +/// // Or you have some action that you want to perform as a side effect +/// fn count(accumulator: &mut usize, slice: &[T], targ: T) { +/// *accumulator += slice.iter() +/// .filter(|e| *e == &targ) +/// .count(); +/// } +/// +/// // Lets say, you also have a function that actually modifies the value +/// fn rot2(slice: [u8; N]) -> [u8; N] { +/// let it = slice.iter() +/// .cycle() +/// .skip(2) +/// .take(N); +/// +/// let mut ret = [0u8; N]; +/// for (no, elm) in it.enumerate() { +/// ret[no] = *elm; +/// } +/// +/// ret +/// } +/// +/// // Then these function are kind of clunky to use in an expression; +/// // it can be done, but the resulting code is a bit verbose +/// let mut accu = 0; +/// assert_eq!(b"llo_WorldHe", &{ +/// let mut buf = b"Hello World".to_owned(); +/// count(&mut accu, &buf, b'l'); +/// replace(&mut buf, b' ', b'_'); +/// rot2(buf) +/// }); +/// assert_eq!(accu, 3); +/// +/// // Instead you could use mutating for a slightly prettier syntax, +/// // but this makes only sense if you want to apply a single action +/// assert_eq!(b"Hello_World", +/// &mutating(b"Hello World".to_owned(), |buf| +/// replace(buf, b' ', b'_'))); +/// +/// // The same is the case for sideeffect() +/// assert_eq!(b"Hello World", +/// &sideeffect(b"Hello World".to_owned(), |buf| +/// count(&mut accu, buf, b'l'))); +/// assert_eq!(accu, 6); +/// +/// // Calling rot2 on its own is straightforward of course +/// assert_eq!(b"llo WorldHe", &rot2(b"Hello World".to_owned())); +/// +/// // These operations can be conveniently used in a method chain +/// // by using the extension traits. +/// // +/// // This is also quite handy if you just need to +/// // modify a value in a long method chain. +/// // +/// // Here apply() also comes in quite handy, because we can use it +/// // to modify the value itself (turning it into a reference). +/// assert_eq!(b"llo_WorldHe", +/// b"Hello World" +/// .to_owned() +/// .sideeffect(|buf| count(&mut accu, buf, b'l')) +/// .mutating(|buf| replace(buf, b' ', b'_')) +/// .apply(rot2) +/// .borrow() as &[u8]); +/// assert_eq!(accu, 9); +/// +/// // There is also the mutating_mut variant, which can operate on any mutable reference; +/// // this is mainly useful in a method chain if you are dealing with a mutable reference. +/// // +/// // This example is quite artificial though. +/// assert_eq!(b"llo_WorldHe", +/// b"hello world" +/// .to_owned() +/// .mutating(|buf| +/// // Can not use sideeffect_ref at the start, because it drops the mut reference +/// // status +/// buf.sideeffect_mut(|buf| count(&mut accu, buf, b'l')) +/// .mutating_mut(|buf| replace(buf, b' ', b'_')) +/// .mutating_mut(|buf| replace(buf, b'h', b'H')) +/// .mutating_mut(|buf| replace(buf, b'w', b'W')) +/// // Using rot2 is more complex now +/// .mutating_mut(|buf| { +/// *buf = rot2(*buf); +/// }) +/// // Can use sideeffect_ref at the end, because we no longer need +/// // the &mut reference +/// .sideeffect_ref(|buf| count(&mut accu, *buf, b'l')) +/// // And we can use apply to fix the return value – if we really want to go +/// // crazy and avoid using a {} block +/// .apply(|_| ()) +/// // [crate::mem::DiscardResult::discard_result] does the same job and it is more explicit. +/// .discard_result()) +/// .borrow() as &[u8]); +/// assert_eq!(accu, 15); +/// ``` +pub fn mutating(mut v: T, mut f: F) -> T where F: FnMut(&mut T), { @@ -6,10 +120,22 @@ where v } +/// Mutating values on the fly in a method chain pub trait MutatingExt { + /// Mutating values on the fly in a method chain (owning) + /// + /// # Examples + /// + /// See [mutating]. fn mutating(self, f: F) -> Self where F: FnMut(&mut Self); + + /// Mutating values on the fly in a method chain (non-owning) + /// + /// # Examples + /// + /// See [mutating]. fn mutating_mut(&mut self, f: F) -> &mut Self where F: FnMut(&mut Self); @@ -32,6 +158,11 @@ impl MutatingExt for T { } } +/// Apply a sideeffect using some value in an expression +/// +/// # Examples +/// +/// See [mutating]. pub fn sideeffect(v: T, mut f: F) -> T where F: FnMut(&T), @@ -40,13 +171,29 @@ where v } +/// Apply sideeffect on the fly in a method chain pub trait SideffectExt { + /// Apply sideeffect on the fly in a method chain (owning) + /// + /// # Examples + /// + /// See [mutating]. fn sideeffect(self, f: F) -> Self where F: FnMut(&Self); + /// Apply sideeffect on the fly in a method chain (immutable ref) + /// + /// # Examples + /// + /// See [mutating]. fn sideeffect_ref(&self, f: F) -> &Self where F: FnMut(&Self); + /// Apply sideeffect on the fly in a method chain (mutable ref) + /// + /// # Examples + /// + /// See [mutating]. fn sideeffect_mut(&mut self, f: F) -> &mut Self where F: FnMut(&Self); @@ -77,11 +224,37 @@ impl SideffectExt for T { } } +/// Just run the function +/// +/// This is occasionally useful; in particular, you can +/// use it to control the meaning of the question mark operator. +/// +/// # Examples +/// +/// ``` +/// use rosenpass_util::functional::run; +/// +/// fn add_and_mul(a: Option, b: Option, c: anyhow::Result, d: anyhow::Result) -> u32 { +/// run(|| -> anyhow::Result { +/// let ab = run(|| Some(a? * b?)).unwrap_or(0); +/// Ok(ab + c? + d?) +/// }).unwrap() +/// } +/// +/// assert_eq!(98, add_and_mul(Some(10), Some(9), Ok(3), Ok(5))); +/// assert_eq!(8, add_and_mul(None, Some(15), Ok(3), Ok(5))); +/// ``` pub fn run R>(f: F) -> R { f() } +/// Apply a function to a value in a method chain pub trait ApplyExt: Sized { + /// Apply a function to a value in a method chain + /// + /// # Examples + /// + /// See [mutating]. fn apply(self, f: F) -> R where F: FnOnce(Self) -> R;