Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does `.map_err(|_| ())` do in this web-sys canvas example?

I'm reading through the canvas hello world example in the wasm-bindgen docs, and I'm confused about this part of the code:

#[wasm_bindgen(start)]
fn start() {
    let document = web_sys::window().unwrap().document().unwrap();
    let canvas = document.get_element_by_id("canvas").unwrap();
    let canvas: web_sys::HtmlCanvasElement = canvas
        .dyn_into::<web_sys::HtmlCanvasElement>()
        .map_err(|_| ()) // What does this do?
        .unwrap();
    // ...
}

Looking at the function definition for map_err, I see:

impl<T, E> Result<T, E> {
    pub fn map_err<F, O: FnOnce(E) -> F>(self, op: O) -> Result<T, F> {
        match self {
            Ok(t) => Ok(t),
            Err(e) => Err(op(e)),
        }
    }
}

So if I'm reading this right, map_error takes a Result<T,E> and a closure that takes E and returns F. If the Result is Ok, it returns T untouched. If it's Err, it applies the closure and returns the result.

The example in the canvas hello world, though, seems useless. It looks like the dyn_into will return a Result<HtmlCanvasElement,Element>, and then map_err turns that into a Result<HtmlCanvasElement,()> if the Result is Err. However, the next line is .unwrap(), so if the result was Err it will panic before it hits the end of the statement. If the Result is Ok, the map_err won't change the value of the Result.

I'm pretty new to Rust, and I'm trying to understand what the map_err line is accomplishing here. Because the Result is unwrapped in the very next line, it seems like you could remove that entire line of code and nothing would change. Is this just a mistake, or is this code doing something that's not obvious to me?

like image 988
Dacromir Avatar asked Dec 06 '25 15:12

Dacromir


1 Answers

The Err side of Result usually contains a value that represents an error, but in some cases (such as this one) where the original value is consumed by the operation, if the operation fails then the Err side can be used to give you back the original value in case you want to try something else with it.

Your assessment of what map_err is doing here is correct -- it is discarding the Element from the Err variant and replacing it with ().

Note that the panic message Result::unwrap produces in the Err case will include the debug-formatted value contained by the Err variant. By mapping the error to (), this prevents dumping out the debug formatting of the Element, which may not be terribly useful. If nothing else in the program ever debug-formats an Element then this can also allow the optimizer to remove Element's Debug implementation, which would reduce the size of the compiled program.

You might also see this pattern in cases where the value stored in the Err variant does not implement Debug and so unwrap cannot be called until it is replaced with something that does. (Though usually .ok().unwrap() is more succinct. .ok() converts Result<T, E> to Option<T>, discarding the error value, and then .unwrap() invokes Option's unwrap instead of Result's.)

like image 190
cdhowie Avatar answered Dec 08 '25 21:12

cdhowie