How to best emulate Rust's `?` operator?
See original GitHub issueI assume you are familiar with ?
operator in Rust? I’m wondering what is the best way to achieve something similar with neverthrow’s ResultAsync
.
REST APIs often require to execute a chain of requests that depend on each other, i.e., I cannot spawn them independently but need to await a successful result sequentially. In case of an error, I want to translate the error into a different error type. Here’s an example:
type ResponseType = {
some: {
nested: {
field: number
}
},
other: {
nested: {
field: number
}
}
}
async function exampleRequest(arg?: number): Promise<ResponseType> {
return Promise.resolve({
some: {nested: {field: 1}},
other: {nested: {field: 2}},
});
}
function wrap<T>(promise: Promise<T>): ResultAsync<T, Error> {
return ResultAsync.fromPromise(promise, (e) => e as Error);
}
async function requestChain(): Promise<Result<number, string>> {
let aResult = await wrap(exampleRequest())
if (aResult.isErr()) {
return err("something went wrong in request A")
}
let a = aResult.value.some.nested.field
let bResult = await wrap(exampleRequest(a))
if (bResult.isErr()) {
return err("something went wrong in request B")
}
let b = bResult.value.other.nested.field
let cResult = await wrap(exampleRequest(b))
if (cResult.isErr()) {
return err("something went wrong in request C")
}
let c = cResult.value.other.nested.field
return ok(a + b + c)
}
It looks like I’m looking for some kind of unwrapOrReturnError
. In Rust such chains simplify a lot when using the ?
operator. Any thoughts what would be a nice solution with neverthrow?
I feel simply using map
doesn’t scale so well, because each request would add a level of indentation (in the specific API case I had a chain of 8 requests). Using andThen
seems tricky as well, because the steps have complex interdependencies, e.g. I would need the results of step 3+4 in step 7, but step 1+3 in step 8 or so.
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (6 by maintainers)
Top GitHub Comments
Here is a full demo of the hoisting approach:
A few notes:
?
operator in the sense that the function short-circuits on error, all values are naturally unwrapped, and it easily scales to a large number to-be-unwrapped values.async
.startChain
is obviously not necessary, I just liked to make the first request syntactically identical to the others.andThen
could also be a plainmap
returning just the value.WrappedError
type that allows me to introduce my own error message (which could be exposed to the user) along with storing the original errors (which I would use for debug logging or so).EDIT: One of your examples inspired me to re-write that using destructuring, which also looks pretty good:
Yes, exactly. I assume the nested tuple approach would work with a similar destructuring on input side, but would perform the “accumulation” automatically, i.e., it spares the explicit
map
’s in the last example.(From my perspective the current API allows for a good enough approximation of the
?
operator, so feel free to close the issue.)I added a new issue (https://github.com/supermacro/neverthrow/issues/186) to track the
*Collect
api’s. Not yet sure if it can even be done in a way that allows for flat tuples.