pub fn amb<T, E, F>(choices: &[F]) -> Result<T, E>
where
F: Fn() -> Result<T, E>,
{
let mut last_err = None;
for choice in choices {
match choice() {
Ok(v) => return Ok(v),
Err(e) => last_err = Some(e),
}
}
Err(last_err.expect("amb called with empty choices"))
}
pub fn explore<T, E, F>(choices: &[F]) -> Vec<T>
where
F: Fn() -> Result<T, E>,
{
choices.iter().filter_map(|f| f().ok()).collect()
}
pub fn explore_with<T, R, E, F, C>(choices: &[F], combiner: C) -> Result<R, E>
where
T: std::fmt::Debug,
F: Fn() -> Result<T, E>,
C: FnOnce(Vec<T>) -> R,
{
let results = explore(choices);
if results.is_empty() {
Err(choices[0]().unwrap_err())
} else {
Ok(combiner(results))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_amb_first_success() {
let choices: Vec<fn() -> Result<i32, &'static str>> =
vec![|| Err("fail 1"), || Ok(42), || Ok(99)];
let result = amb(&choices);
assert_eq!(result, Ok(42));
}
#[test]
fn test_amb_all_fail() {
let choices: Vec<fn() -> Result<i32, &'static str>> =
vec![|| Err("fail 1"), || Err("fail 2")];
let result = amb(&choices);
assert!(result.is_err());
}
#[test]
fn test_explore_all() {
let choices: Vec<fn() -> Result<i32, &'static str>> = vec![|| Ok(1), || Ok(2), || Ok(3)];
let results = explore(&choices);
assert_eq!(results.len(), 3);
}
}