1. Serial map
Model: one thread, one process. Features: tenferro (for the image only). Example: serial_mandelbrot.
hataori::map is the semantic baseline for every other model: it applies a fallible callback to a Vec<T> in order and returns a Vec<U> in the same order. There are no Send, Sync, 'static, or serde requirements, so the callback can borrow anything, including Rc or RefCell state.
Render the image
/// Render every column in input order on the calling thread.
///
/// `map` takes a `Vec<T>` and a fallible callback and returns a `Vec<U>` in
/// the same order, or the first error together with the failing index. The
/// callback borrows `param`, `x`, and `y` from the caller's stack.
fn render(param: &Param) -> Result<Vec<Vec<i64>>, MapError> {
let (x, y) = param.make_axes();
let columns: Vec<usize> = (0..param.width).collect();
map(columns, |col_idx| {
Ok::<_, String>(mandelbrot_common::compute_column(param, x[col_idx], &y))
})
}The items are the column indices; the callback borrows the parameters and the axes and returns one column. map evaluates the columns one at a time on the calling thread.
Errors stop at the first failure
/// `map` stops at the first error and reports its zero-based input index.
///
/// The callback error type only needs `Display`; `MapError` keeps the
/// index and the `Display` output (truncated to 4096 bytes).
fn reject_columns_outside(param: &Param, limit: usize) -> MapError {
let (x, y) = param.make_axes();
let mut evaluated = 0_usize;
let error = map((0..param.width).collect::<Vec<usize>>(), |col_idx| {
evaluated += 1;
if col_idx >= limit {
return Err(format!("column {col_idx} is outside the requested range"));
}
Ok(mandelbrot_common::compute_column(param, x[col_idx], &y))
})
.expect_err("column `limit` must fail");
assert_eq!(error.index(), limit);
// Columns after the failing one were never evaluated.
assert_eq!(evaluated, limit + 1);
error
}- The error type only needs
Display.MapErrorstores the failing input’s index and theDisplayoutput, truncated to 4096 bytes at a UTF-8 character boundary. mapstops at the first error. The counter shows that the column afterlimitwas never evaluated, and inputs after the failure are simply dropped.- The callback is
FnMut, so it can mutate captured state such asevaluated.
Source: examples/serial_mandelbrot.rs
Run
cargo run --release --no-default-features --features tenferro \
--example serial_mandelbrot -- --width 1024 --height 1024The example prints the elapsed time, the first error from the failing run, and writes mandelbrot_serial.png.
Next: 2. Rayon map_in keeps this contract and adds a thread pool.