[SOLVED] Conditional with two different types

In this code:

///Get one person
impl Handler<GetPerson> for DbExecutor {
    type Result = Result<Vec<models::Person>, ServiceError>;

    fn handle(&mut self, msg: GetPerson, _: &mut Self::Context) -> Self::Result {
        use self::schema::people::dsl::*;

        let person_name = &msg.name;

        let conn: &PgConnection = &self.0.get().expect("Error to connect to database");

        let items = people
            .filter(name.eq(person_name))
            .load::<models::Person>(conn)
            .map_err(|error| ServiceError::InternalServerError(format!("{:#?}", error)))?;

        let person = if !items.is_empty() {
                items
            } else {
            ServiceError::NotFound("{} not found in database", person_name)
            }
        };

        Ok(person)
    }
}

I try to return ServiceError if the vector is empty and the vector is not however I don't know how can I do it because to use the if Pattern I need to return always the same type if not the compiler will throw the error if and else have incompatible types. Also I would like to know how can I check if items return ServiceError instead of the vector when it loads from the database

Hi,

I can't test the code but I think this should work:

///Get one person
impl Handler<GetPerson> for DbExecutor {
    type Result = Result<Vec<models::Person>, ServiceError>;

    fn handle(&mut self, msg: GetPerson, _: &mut Self::Context) -> Self::Result {
        use self::schema::people::dsl::*;

        let person_name = &msg.name;

        let conn: &PgConnection = &self.0.get().expect("Error to connect to database");

        let items = people
            .filter(name.eq(person_name))
            .load::<models::Person>(conn)
            .map_err(|error| ServiceError::InternalServerError(format!("{:#?}", error)))?;

        if !items.is_empty() {
            Ok(items)
        } else {
            Err(ServiceError::NotFound("{} not found in database", person_name))
        }
    }
}

By wrapping items in Ok() both cases will return the same type.

1 Like

you can use the if expression as you final expression in the function like this

if !items.is_empty() {
        Ok(items)
    } else {
        Err(ServiceError::NotFound("{} not found in database", person_name))
    }
}

or in other cases where the if block is not the final expression you could do

let person = if !items.is_empty() {
        items
    } else {
        Err(ServiceError::NotFound("{} not found in database", person_name))?
    }
}
1 Like

The other suggestions are better for your case, but note that you can return from an if/else arm:

let person = if !items.is_empty() {
     items
} else {
    return Err(ServiceError::NotFound("{} not found in database", person_name))
};
 Ok(person)
1 Like

Thank you a lot @stefan-k and the rest who answer me this post, it works. Another question, if I get error from the database for example that the table exist, how could I check it? Because I don't think that people will return me a Vec in that case, I think it would return me the type SeviceError::InternalServerError.

let items = people
            .filter(name.eq(person_name))
            .load::<models::Person>(conn)
            .map_err(|error| ServiceError::InternalServerError(format!("{:#?}", error)))?;

You have the underlying error in the error arg given to the map_err combinator you're using - you can inspect it inside the closure. Alternatively, you can match on it:

match people.filter(name.eq(person_name)).load::<models::Person>(conn) {
    Ok(items) => ...
    Err(e) => // inspect the error `e` here, depending on its type, it may expose the root problem, like table missing

If you provide a bit more detail on what exactly you'd like to do, we can give more specific advice.

1 Like

Hello @vitalyd thank you for your answer. If I write to try to handler two different kind of errors the compiler fail:

match people
            .filter(name.eq(person_name))
            .load::<models::Person>(conn) {
                Ok(items) => {
                    if !items.is_empty() {
                        Ok(items)
                    } else {
                        Err(ServiceError::NotFound(format!("{} not found in database", person_name)))
                    }
                },
                Err(e) => ServiceError::InternalServerError(format!("{:#?}", error))
            }

error:

note: expected type `std::result::Result<std::vec::Vec<models::Person>, error::ServiceError>`
   found type `error::ServiceError`
label: expected enum `std::result::Result`, found enum `error::ServiceError`

Sorry I forgot to write Err :sweat_smile:
this works:

match people
            .filter(name.eq(person_name))
            .load::<models::Person>(conn) {
                Ok(items) => {
                    if !items.is_empty() {
                        Ok(items)
                    } else {
                        Err(ServiceError::NotFound(format!("{} not found in database", person_name)))
                    }
                },
                Err(error) => Err(ServiceError::InternalServerError(format!("{:#?}", error)))
            }

thank you a lot @vitalyd