Handling errors in closures

Hi all,

When mapping a list of RefCell pointers to some object I want to borrow the value, check a method, and continue.
However, error handling in this case is proving quite difficult.
Ideally I'd use the "?" operator, but as that doesn't work in a closure, I'm not sure where to take this next.

This is a minimal case for what I'm looking for:

struct SomeStruct { value: i32 }

let list_of_stuff: Vec<Rc<RefCell<SomeStruct >>> = Vec::new(); // Data goes here

let values:Vec<i32> = list_of_stuff.iter().map(|s| s.try_borrow()?.value).collect();

the ? operator must be used within a context where the error can be propagated. your code collect the result into a Vec<i32>, which doesn't allow the closure to fail. try this;

    let values: Result<Vec<i32>, BorrowError> = list_of_stuff
        .iter()
        .map(|s| Ok(s.try_borrow()?.value))
        .collect();
2 Likes

Aaah I see, I had assumed that would return a vec of Results, but this makes sense.

Thank you!

you had explicitly annotated the return value be Vec<i32> so the closure must have a return type of i32, that's why you can't use ? operator in your original example.

if you only annotate the container to be Vec<_>, but don't specify what's the data type, you can let the type checker infer a type for you which will be a Result<_, _> type:

    // inferred type is `Vec<Result<i32, BorrowError>>
    let values = list_of_stuff
        .iter()
        .map(|s| s.try_borrow().map(|r| r.value))
        .collect::<Vec<_>>();

in this case, you cannot use ? without annotation of the Err type because the ? operator desugars to a call to Into::into() on the Err case. if you explicitly spell out the omitted types in the closure, or just rewrite the closure as a function, you'll have better understanding how the type inference work:

let values = list_of_stuff
.iter()
.map(|s: &Rc<RefCell<SomeStruct>>| -> Result<i32, BorrowError> {
        // you can use `?` operator because the closure return type is `Result`
        let s: Ref<'_, SomeStruct> = s.try_borrow()?;
        Ok(s.value)
 })
.collect::<Vec<_>>();

// equivalently
fn try_borrwo_and_extract_value(s: &Rc<RefCell<SomeStruct>>) -> Result<i32, BorrowError> {
    let s = s.try_borrow()?;
    Ok(s.value)
}

when the yielded item type of an iterator is a Result<T, E>, this is a special case where you can collect it into Container<Result<T, E>> as normal, or you collect into a Result<Container<T>, E>. in the latter case, collect will short circuit and stop early if an error is encountered which gives lazy iterators benefits.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.