// 1020: try_fold โ Fold that short-circuits on error
// Approach 1: Iterator::try_fold
fn sum_positive(numbers: &[i64]) -> Result<i64, String> {
numbers.iter().try_fold(0i64, |acc, &n| {
if n < 0 {
Err(format!("negative number: {}", n))
} else {
Ok(acc + n)
}
})
}
// Approach 2: try_fold with accumulator transformation
fn concat_limited(strings: &[&str], max_len: usize) -> Result<String, String> {
strings.iter().try_fold(String::new(), |mut acc, &s| {
acc.push_str(s);
if acc.len() > max_len {
Err(format!("result too long: {} > {}", acc.len(), max_len))
} else {
Ok(acc)
}
})
}
// Approach 3: try_fold vs regular fold comparison
fn product_no_overflow(numbers: &[i64]) -> Result<i64, String> {
numbers.iter().try_fold(1i64, |acc, &n| {
acc.checked_mul(n)
.ok_or_else(|| format!("overflow at {} * {}", acc, n))
})
}
fn main() {
println!("sum [1,2,3]: {:?}", sum_positive(&[1, 2, 3]));
println!("sum [1,-2,3]: {:?}", sum_positive(&[1, -2, 3]));
println!("concat: {:?}", concat_limited(&["hello", " ", "world"], 20));
println!("product: {:?}", product_no_overflow(&[2, 3, 4]));
println!("Run `cargo test` to verify all examples.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sum_all_positive() {
assert_eq!(sum_positive(&[1, 2, 3]), Ok(6));
}
#[test]
fn test_sum_negative_fails() {
let result = sum_positive(&[1, -2, 3]);
assert_eq!(result, Err("negative number: -2".to_string()));
}
#[test]
fn test_sum_empty() {
assert_eq!(sum_positive(&[]), Ok(0));
}
#[test]
fn test_concat_ok() {
assert_eq!(
concat_limited(&["hello", " ", "world"], 20),
Ok("hello world".to_string())
);
}
#[test]
fn test_concat_too_long() {
let result = concat_limited(&["hello", " ", "world!!!!!!!!!!!!"], 10);
assert!(result.is_err());
assert!(result.unwrap_err().contains("too long"));
}
#[test]
fn test_product_ok() {
assert_eq!(product_no_overflow(&[2, 3, 4]), Ok(24));
}
#[test]
fn test_product_overflow() {
let result = product_no_overflow(&[i64::MAX, 2]);
assert!(result.is_err());
assert!(result.unwrap_err().contains("overflow"));
}
#[test]
fn test_short_circuit_proof() {
// try_fold stops processing after first error
let mut count = 0;
let result = [1, -2, 3, 4, 5].iter().try_fold(0, |acc, &n| {
count += 1;
if n < 0 { Err("negative") } else { Ok(acc + n) }
});
assert!(result.is_err());
assert_eq!(count, 2); // only processed [1, -2], stopped
}
#[test]
fn test_try_fold_vs_fold() {
// Regular fold processes everything
let sum = [1, 2, 3].iter().fold(0, |acc, n| acc + n);
assert_eq!(sum, 6);
// try_fold can bail early
let result = [1, 2, 3].iter().try_fold(0, |acc, &n| {
if acc + n > 4 { Err("too big") } else { Ok(acc + n) }
});
assert!(result.is_err());
}
}
(* 1020: try_fold โ Fold that short-circuits on error *)
(* Approach 1: Manual try_fold *)
let try_fold f init lst =
let rec aux acc = function
| [] -> Ok acc
| x :: rest ->
match f acc x with
| Error e -> Error e
| Ok acc' -> aux acc' rest
in
aux init lst
(* Approach 2: Using Seq for lazy evaluation *)
let try_fold_seq f init seq =
let rec aux acc seq =
match seq () with
| Seq.Nil -> Ok acc
| Seq.Cons (x, rest) ->
match f acc x with
| Error e -> Error e
| Ok acc' -> aux acc' rest
in
aux init seq
(* Example: sum numbers, but reject negatives *)
let sum_positive acc n =
if n < 0 then Error (Printf.sprintf "negative number: %d" n)
else Ok (acc + n)
(* Example: build string, but limit length *)
let concat_limited acc s =
let result = acc ^ s in
if String.length result > 20 then Error "result too long"
else Ok result
let test_try_fold () =
assert (try_fold sum_positive 0 [1; 2; 3] = Ok 6);
(match try_fold sum_positive 0 [1; -2; 3] with
| Error e -> assert (e = "negative number: -2")
| Ok _ -> assert false);
(* Short-circuits: [3] never processed *)
assert (try_fold sum_positive 0 [] = Ok 0);
Printf.printf " Approach 1 (list try_fold): passed\n"
let test_try_fold_seq () =
let seq = List.to_seq [1; 2; 3] in
assert (try_fold_seq sum_positive 0 seq = Ok 6);
let seq = List.to_seq [1; -2; 3] in
(match try_fold_seq sum_positive 0 seq with
| Error _ -> ()
| Ok _ -> assert false);
Printf.printf " Approach 2 (seq try_fold): passed\n"
let test_concat () =
assert (try_fold concat_limited "" ["hello"; " "; "world"] = Ok "hello world");
(match try_fold concat_limited "" ["hello"; " "; "world!!!!!!!!!!!!!!!!"] with
| Error e -> assert (e = "result too long")
| Ok _ -> assert false);
Printf.printf " Concat example: passed\n"
let () =
Printf.printf "Testing try_fold:\n";
test_try_fold ();
test_try_fold_seq ();
test_concat ();
Printf.printf "โ All tests passed\n"