Peter Delevoryas
Peter Delevoryas

Reputation: 573

C++ equivalent of Rust's Result<T, E> type?

I like using std::experimental::optional in my C++ code, but the problem is value_or requires the default value to be of the same type as the optional's value.

This doesn't work very well when I want an optional that either contains an int or contains an error message.

I guess I could use a union struct that has a boolean to indicate if the value is there or it's an error, but it sure would be nice if C++ just had a Result<T, E> type like Rust.

Is there any such type? Why hasn't Boost implemented it?

Result is really much more useful than Option, and surely the people at Boost are aware of its existence. Maybe I'll go read the Rust implementation and then copy it to C++?

Ex:

// Function either returns a file descriptor for a listening socket or fails
// and returns a nullopt value.
// My issue: error messages are distributed via perror.
std::experimental::optional<int> get_tcp_listener(const char *ip_and_port);
// You can use value_or to handle error, but the error message isn't included!
// I have to write my own error logger that is contained within
// get_tcp_listener. I would really appreciate if it returned the error
// message on failure, rather than an error value.
int fd = get_tcp_listener("127.0.0.1:9123").value_or(-1);
// Rust has a type which does what I'm talking about:
let fd = match get_tcp_listener("127.0.0.1:9123") {
    Ok(fd) => fd,
    Err(msg) => { log_error(msg); return; },
}

Upvotes: 46

Views: 29835

Answers (5)

Nir Friedman
Nir Friedman

Reputation: 17704

What you are looking for is exactly Alexandrescu's Expected. I recommend listening to his talk for an in depth understanding: https://www.youtube.com/watch?v=kaI4R0Ng4E8. He actually goes through the implementation line by line, and you can easily write it yourself and use it well after that.

Variant is a more general tool, it can be coerced to do what you want but you're better off with expected.

Please see

Upvotes: 13

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275740

In , optional<T> is an asymmetric type safe union of T and nothingness (nullopt_t). You can query if it has a T with explicit operator bool, and get the T out with unary *. The asymmetry means that optional "prefers" to be a T, which is why unqualified operations (like * or operator bool) refer to its Tness.

In variant<A,B,C> from paper n4218 is a symmetric type safe union of A, B and C (etc). boost::variant is always engaged, and std::variant is almost always engaged (in order to preserve some exception guarantees, it can become valueless by exception if the types it store don't have the right exception semantics).

As it is symmetric, there is no unique type for unary * to return, and explicit operator bool cannot say much of interest, so neither are supported.

Instead, you have to visit it, or query it for particular types.

In std::expected<T, E> from paper n4015 is an asymmetric type-safe union. It is either a T, or an E. But like optional, it "prefers" to be a T; it has an explicit operator bool that tells you if it is a T, and unary * gets the T.

In a sense, expected<T,E> is an optional<T>, but when empty instead of wasting the space it stores an E, which you can query.

Result<T,E> seems close to expected<T,E> (note that as of n4015, the order of parameters are swapped compared to Result, but the published version did not).

Upvotes: 68

gustafbstrom
gustafbstrom

Reputation: 1933

Since this was asked, the C++23 standard's std::expected does exactly this. A summarizing quote from Cpp Reference:

The class template std::expected provides a way to store either of two values. An object of std::expected at any given time either holds an expected value of type T, or an unexpected value of type E. std::expected is never valueless.

Upvotes: 2

S.R
S.R

Reputation: 2751

If not only boost is involved u can use result. This is nice single header container.

Upvotes: 11

Alan Stokes
Alan Stokes

Reputation: 18974

optional by design either contains a value of some type or nothing.

You may be looking for something like Boost::Variant.

This is not yet part of the standard library, although something like it may be eventually.

Upvotes: 4

Related Questions