I have a method with a parameter of type Result
. I like chaining a lot, so I use and_then
on the parameter. At some point, I want to return the whole method from inside the and_then
conditionally (so there is a chance another and_then
method can be called):
enum Good {
Ok,
Good,
VeryGood,
}
enum Pizza {
Tomato,
Pineapple,
}
enum Burger {
Cow,
}
enum Food {
Pizza(Pizza),
Burger(Burger),
}
fn main() {}
fn s(r: Result<Good, ()>) -> Result<Food, ()> {
r.and_then(|o| {
match o {
// Should be called in the next and_then block
Good::Ok => Ok(Pizza::Tomato),
// Should be called in the next and_then block
Good::Good => Ok(Pizza::Pineapple),
Good::VeryGood => {
// I am done. Don't call the next and_then block, but rather return the whole value to the caller.
return Ok(Food::Burger(Burger::Cow));
}
}
})
.and_then(|p: Pizza| {
// At this point, the closure input value should be pizza, because that's the only returned value
Ok(Food::Pizza(p))
})
}
I get all kinds of compiler errors:
error[E0308]: mismatched types
--> src/main.rs:30:27
|
30 | return Ok(Food::Burger(Burger::Cow));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Pizza`, found enum `Food`
|
= note: expected type `Pizza`
found type `Food`
I hope there is a way to make it compile. I could break down the method and get rid of the and_then
, but maybe there is a way with and_then
.
In my real code, I have more and_then
's which maps types to other types, error mapping etc, so this is a simplified reproduction path of the problem I am facing.
This is copy-pasted code from my codebase, showing the multiple and_then
. I am mapping errors from external libraries into my own error type, so errors can be returned automatically if there are any. I want to keep changing the type that I got from the and_then
so I can eventually get type User
(although it is currently not working). An option is to not chain the blocks and create separate values, but I was hoping I could directly return values to the caller inside a closure.
db_session
.query_with_values(query, values)
.map_err(|e| {
error!("{:?}", e);
TechnicalServerError::SERVER_RETRY
})
.and_then(|f| {
f.get_body().map_err(|e| {
error!("{:?}", e);
TechnicalServerError::SERVER_RETRY
})
})
.and_then(|b| b.into_rows().ok_or(TechnicalServerError::SERVER_RETRY))
.and_then(|mut c| {
if let Some(row) = c.pop() {
User::try_from_row(row).map_err(|e| {
error!("{:?}", e);
TechnicalServerError::SERVER_INVALID
})
} else {
return Ok(Login::Other(LoginResult::UNKNOWN_USER));
}
})
.and_then(|u| {
// 'u' should be of type 'user' at this point
// some user code here...
})