How to deal with Result<T,E> inside the closure function in Rust?

I have a nested object in mongodb that I want to deserialize/decode back into a struct.
Here's the structure :

pub struct ItemUnit {
    /// unit's name like PCS, DOZEN, PACK, etc
    pub name: String,

    /// denote this unit value
    pub multiplier: f64,

    /// item's price in this unit
    pub price: Option<f64>,
}

pub struct Item {
    /// item's name
    pub name: String,

    /// available unit types available for this item, in an array format
    pub units: Vec<ItemUnit>,
}

as you can see the struct is nested and units is an array (Vec<ItemUnit>).

As Rust have a culture of "not letting you go with error possibility", this quickly becomes tricky. In Rust mongodb driver, you have to deserialize/decode them back from 'bson' document into Item. Each deserialize operation returns Result<> since there's a possibility of error in it.

I have a problem with nested object with an array of another object in it:

// doc is the data from a single `Item`
// since `get_array` might throw a ValueAccess error, I map it into my own custom Error type
// so `units_bson` is now a `Vec<mongodb::bson::Bson>` type
let units_bson = doc.get_array("units").map_err(Self::map_mongodb_err)?;

// in the next step, I need to decode the mongodb::bson::Bson for each element in this array
/// here's I use map() with closure
let units: Vec<ItemUnit> = units_bson.iter().map(|u| { 
   
   // get_str() returns Result<T,E> but since ItemUnit only accepts a string not Result<> type
   // I had to handle the error possibilities here in the closure/map function
   // but the ? operator only available on a function that returns `Result` or `Option` 
   //  (or another type that implements `Try`)
   let name = u.as_document().unwrap().get_str("name").map_err(Self::map_mongodb_err);
   return ItemUnit { name, multiplier, p_buy };

}).collect();

So my question is how do you catch an error inside a closure?

Or is there any other workaround like try-catch block that can also catch any error inside a closure ?

You can collect an iterator of Results into another Result:

let units: Vec<ItemUnit> = units_bson.iter().map(|u| {
    let name = u.as_document().unwrap().get_str("name").map_err(Self::map_mongodb_err)?;
    Ok(ItemUnit { name, multiplier, p_buy })
}).collect()?;

Here the closure returns a Result<ItemUnit, YourErrorType>, and those Results are collected into a single Result<Vec<ItemUnit>, YourErrorType> (which is Ok if all of the original values were Ok, and the same as the first Err otherwise), which you turn into Vec<ItemUnit> with another application of ?.

Also, I strongly suggest you not use [binary] floating-point types like f64 to represent money. You should use some kind of fixed-point representation with sensible rounding behavior [edit: not confident that this is actually correct], or just find a crate that takes care of such things for you.

1 Like

Thank you. But I think this answer still lacks "type enforcer" or explicit return type expression somewhere around the collect() function since I get this error. It seems that Rust cannot infer the type that I want to return

type annotations needed

cannot infer type

note: cannot satisfy `_: std::ops::Try`
note: required by `std::ops::Try::into_result`
help: consider specifying the type argument in the method call: `::<B>`rustc(E0283)

mod.rs(223, 14): cannot infer type

Ah, yeah, I can see how this might hit type inference issues. Does it work if you annotate the type of the closure like this?

|u: _| -> Result<ItemUnit, YourErrorType> {
    // ...
}

You could also try turbofishing the collect:

units_bson.iter().map(/* ... */).collect::<Result<_, YourErrorType>>()?

It turns out I need to use this units_bson.iter().map(/* ... */).collect::<Result<_, CustomError>>()?

1 Like

Would you mind to share to me how or what kind of crate that you suggest me to use to store money? Something like currency - Rust ?
Thank you

I'm not an expert on this stuff, but the rust_decimal library appears to be stable (post-1.0) and actively maintained, and is described as being suitable for financial calculations.

1 Like