-
-
Save igalic/6261f3f0124d2115cf88c31f7916a706 to your computer and use it in GitHub Desktop.
match req { | |
Ok(mut res) => { | |
if let Ok(text) = &res.text() { | |
if let Ok(ap_sign) = serde_json::from_str::<ApSignature>(text) { | |
if let Ok(mut json) = serde_json::from_str::<CustomPerson>(text) { | |
json.custom_props = ap_sign; // without this workaround, publicKey is not correctly deserialized | |
Some(json) | |
} else { | |
None | |
} | |
} else { | |
None | |
} | |
} else { | |
None | |
} | |
} | |
Err(e) => { | |
println!("User fetch error: {:?}", e); | |
None | |
} | |
} |
Although they are Result
, if you really want to return an Option
, just add .ok()
before each ?
(it works with Option
too). Keeping the return types the same and the print macro (untested):
let mut res = req
.or_else(|e| {
println!("User fetch error: {:?}", e);
Err(e) })
.ok()?;
let text = &res.text().ok()?;
let mut json = serde_json::from_str::<CustomPerson>(text).ok()?
let ap_sign = serde_json::from_str::<ApSignature>(text).ok()?;
json.custom_props = ap_sign;
Some(json)
However I would try to return a Result, do the same as the first suggestion and manage the Err from the calling function.
You can use ?
in a match
expiration too.
match req? {
}
Using and_then
:
let stuff = req
.and_then(|res| {
res.text().and_then(|text| {
serde_json::from_str::<ApSignature>(text).and_then(|ap_sign| {
serde_json::from_str::<CustomPerson>(text).map(|mut json| {
json.custom_props = ap_sign;
Ok(json)
})
})
})
})
.map_err(|e| {
println!("User fetch error: {:?}", e);
e
})
.ok();
The early-exit answers with ?
are the same direction as I would go. I generally like to handle my errors as they come up, not save them for later, because all the intervening stuff is irrelevant for the errors. I haven't conducted benchmarking to know whether there is a performance penalty for the (implicit) double-testing using this approach, but if there is a penalty it should be small. So:
let mut res = req.map_err(|e| println!("User fetch error: {:?}", e)).ok()?;
let text = &res.text().ok()?;
let ap_sign = serde_json::from_str::<ApSignature>(text).ok()?;
let mut json = serde_json::from_str::<CustomPerson>(text).ok()?;
json.custom_props = ap_sign;
Some(json)
I haven't conducted benchmarking to know whether there is a performance penalty for the (implicit) double-testing using this approach, but if there is a penalty it should be small.
and_then
is basically a match expression that calls the passed function with the value of T
if self
is Ok(T)
and bypasses self
if it is Err(E)
, so you don't avoid anything there.
The and_then
example doesn't work very well here because the value extraction can't be done in a fully stream-like fashion. To work around that without creating new variables (which is useless in itself) you have to do all that ugly indenting stuff that makes the code more difficult to read. The only thing you gain with that approach is not calling ok()
multiple times, but again, I think the real solution is to change the return type of the function.
BTW I don't think losing information about Err
is a good idea, unless you are micro-optimizing hard on memory usage and transfers for a very good reason.
Actually they are Results so you can use
?
. Something like: