-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a try_unwrap(...) or_return
construct, similar to Rust's ?
operator
#6554
base: master
Are you sure you want to change the base?
Conversation
1375714
to
0e4b5d3
Compare
unwrap { ... } or_return
construct, similar to Rust's ?
operatortry_unwrap { ... } or_return
construct, similar to Rust's ?
operator
try_unwrap { ... } or_return
construct, similar to Rust's ?
operatortry_unwrap(...) or_return
construct, similar to Rust's ?
operator
try_unwrap(...) or_return
construct, similar to Rust's ?
operatortry_unwrap(...) or_return
construct, similar to Rust's ?
operator
maxSaplingAvailable, | ||
maxOrchardAvailable, | ||
orchardOutputs) | ||
) or_return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notice that the map_error
s (two of them in this function) go away because ResolvePayment
returns a tl::expected<ResolvedPayment, AddressResolutionError>
, and AddressResolutionError
is a variant of InputSelectionError
. So this just works, because there is an implicit conversion from each arm of a variant to the variant type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not wedded to the convention I've adopted here of putting the ) or return
at the start of the line, but it does make it easier to see that there is a potential return. I looked at whether it was possible to get Visual Studio to highlight these as keywords, but couldn't find the highlighting config file for C++.
}); | ||
}); | ||
try_unwrap(ValidateAmount(spendable, initialFee) | ||
) or_return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just works with void
types (this was the most complicated part).
orchardOutputs) | ||
) or_return; | ||
|
||
return {std::make_pair(actualPayment, finalFee)}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The addition of braces here isn't essential; just a matter of style because we're constructing a tl::expected
from this pair (as we were before).
}); | ||
try_unwrap(ValidateAmount(spendable, initialFee) | ||
) or_return; | ||
const ResolvedPayment rpayment = try_unwrap(ResolvePayment( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const auto rpayment
would also work here; the try_unwrap
doesn't interfere with type inference. I just prefer it this way. (You do sometimes have to add explicit return types to lambdas when refactoring to use try_unwrap
.)
int v = try_unwrap(ex) or_assert; | ||
return {v+1}; | ||
}; | ||
ASSERT_DEATH(fn(err), "has_value()"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not certain these death tests will work on all platforms (in particular, I don't know if Windows will preserve the assertion text that we check for here). The test assumes NDEBUG
is not defined, which is always the case for zcashd.
`try_unwrap(x) or_return` is roughly equivalent to (using the GCC/clang statement expression extension): ```c++ ({ auto m = (x); if (!m.has_value()) return adapt_error(m); m.value(); // if m has a non-void value_type }) ``` where `adapt_error(m)` preserves the error (or null) status of `m` but may adapt it to the correct value type. `try_unwrap(x) or_return_unexpected(err)` is roughly equivalent to: ```c++ ({ auto m = (x); if (!m.has_value()) return tl::make_unexpected(err); m.value(); // if m has a non-void value_type }) ``` `try_unwrap(x) or_assert` is roughly equivalent to: ```c++ ({ auto m = (x); assert(m.has_value()); m.value(); // if m has a non-void value_type }) ``` These constructs work correctly for any type T that has: * a `value_type` member; * a `bool has_value()` method; * a `T::value_type value()` method that can be called when `has_value()` returns true. This need not exist if `T::value_type` is void. These conditions are met for `tl::expected<T, E>`, the proposed `std::expected<T, E>`, `std::optional<T>`, and potentially your custom type. See the tests in `src/gtest/test_unwrap.cpp` for what is possible in terms of conversion between types. Signed-off-by: Daira Emma Hopwood <daira@jacaranda.org>
e7d1319
to
2af0ba3
Compare
Signed-off-by: Daira Emma Hopwood <daira@jacaranda.org>
2af0ba3
to
100a0e1
Compare
I suggest reviewing with "Hide whitespace" initially. (One of the points of this is to avoid the tab slide to the right caused by nested error handling.) |
After
#include "util/unwrap.h"
,try_unwrap(x) or_return
is roughly equivalent to (using the GCC/clang statement expression extension):where
adapt_error(m)
preserves the error (or null) status ofm
but may adapt it to the correct value type.try_unwrap(x) or_return_unexpected(err)
is roughly equivalent to:try_unwrap(x) or_assert
is roughly equivalent to:These constructs work correctly for any type T that has:
value_type
member;bool has_value()
method;T::value_type value()
method that can be called whenhas_value()
returns true. This need not exist ifT::value_type
is void.These conditions are met for
tl::expected<T, E>
, the proposedstd::expected<T, E>
[*],std::optional<T>
, and potentially your custom type.See the tests in
src/gtest/test_unwrap.cpp
for what is possible in terms of conversion between types.[*] Caveats: type adaption is not enabled for
std::expected
(but could easily be added), andtry_unwrap(x) or_return_unexpected(err)
will always returntl::expected
in the error case.fixes #6552