I'm trying to convert a std::optional into a std::expected using a lambda function.
The code I have written does not compile. The compiler complains that the monadic functions of std::optional must return another std::optional.
Here's my example code, which can be found on Godbolt.
#include <string>
#include <optional>
#include <expected>
std::optional<int> function_which_returns_optional_1() {
return 10;
}
struct ErrorType {
};
int main() {
std::optional<int> optional_value = function_which_returns_optional_1();
std::expected<std::string, ErrorType> result =
optional_value
.and_then(
[](int value) -> std::expected<std::string, ErrorType> {
const auto value_string = std::to_string(value);
return std::expected(value);
}
)
.or_else(
[]() -> std::expected<std::string, ErrorType> {
return std::unexpected(ErrorType{});
}
);
}
If you are familiar with Rust, this code will probably feel very relevant. Rust has a mechanism for translating std::Result types to std::option, and vice-versa. The C++ equivalent of std::Result is std::expected.
If you have written any Rust code, you will probably instinctively understand why I want to do this and what I am trying to do here.
If you are not familiar with Rust, then here's a couple of pieces of contextual information.
std::expected.std::optional rather than std::expected. (Or vice-versa.) In such cases there may be function calls in the call stack which need to translate between the two types. Typically, an optional will become an expected, because a missing value is an error case. (The reverse of expected to optional makes less sense, becuase this reads as if an error is being hidden or discarded.)Is there a way to get this code to compile?
Ideally, I would like to avoid the more verbose, although obvious, solution:
if(optional_value.has_value()) {
const auto actual_value = *optional_value;
const auto result = std::expected<std::string, ErrorType>(std::to_string(actual_value));
return result;
}
else {
const auto error_type = ErrorType{};
const auto result = std::expected<std::string, ErrorType>(std::unexpected(error_type));
return result;
}
You can use transform, which unlike and_then, does not constrain the invocable to return a specialization of std::optional.
The return value from the invocable will be wrapped by transform into an std::optional, but then you can unwrap that with value.
Since you have an std::unexpected case, you can then use value_or.
auto result = optional_value
.transform(
[](int value) -> std::expected<std::string, ErrorType> {
return std::to_string(value);
})
.value_or(std::unexpected(ErrorType{}));
demo
You could revise your second code snippet to make it much more concise.
std::expected<std::string, ErrorType> optional_to_expected(const std::optional<int>& opt) {
if (opt) {
return std::to_string(*opt);
}
return std::unexpected(ErrorType());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With