How to Migrate from std::optional to absl::StatusOr for Error Handling
To migrate from std::optional to absl::StatusOr, replace your return type with absl::StatusOr<T>, return values directly on success, return absl::Status errors on failure, and update callers to check ok() before accessing the value via operator* or value().
When building robust C++ applications with the Abseil library, error handling requires more expressiveness than std::optional can provide. While std::optional only indicates whether a value exists, absl::StatusOr<T> carries a rich error payload that explains why an operation failed. This guide shows you how to migrate existing code using the actual implementation details found in absl/status/statusor.h.
Why Replace std::optional with absl::StatusOr?
absl::StatusOr<T> is a union that holds either a value of type T or an absl::Status object. Unlike std::optional<T>, which only records whether a value is present, StatusOr also conveys detailed error information when a value is absent.
| Feature | std::optional<T> |
absl::StatusOr<T> |
|---|---|---|
| Success indication | has_value() |
ok() |
| Error information | None (silent absence) | Full absl::Status with code and message |
| Value access | operator*, operator->, value() |
operator*, operator->, value(), value_or() |
| Default construction | Empty optional | Non-OK status with StatusCode::kUnknown |
Because absl::optional in this repository is merely a thin alias for std::optional (defined in absl/types/optional.h), migrating to absl::StatusOr adds zero runtime overhead while giving you expressive error diagnostics and integration with Abseil’s logging utilities.
Step-by-Step Migration Guide
1. Update Headers and Return Types
Replace <optional> or absl/types/optional.h with the StatusOr headers, then change your function signature.
// Old
#include <optional>
std::optional<Foo> ParseFoo(const std::string& s);
// New
#include "absl/status/statusor.h"
#include "absl/status/status.h"
absl::StatusOr<Foo> ParseFoo(const std::string& s);
2. Modify Function Implementation
Return success values directly through implicit conversion. Return errors using absl::Status factory functions.
absl::StatusOr<Foo> ParseFoo(const std::string& s) {
if (s.empty()) {
return absl::InvalidArgumentError("empty input string");
}
// Success path - implicit conversion to StatusOr<T>
return Foo(s);
}
3. Update Caller Code
Replace has_value() checks with ok() calls. Access the value using operator* or operator-> (recommended), or value() if you want exception guarantees.
// Old optional approach
if (auto opt = ParseFoo(s); opt) {
opt->DoSomething();
}
// New StatusOr approach
absl::StatusOr<Foo> result = ParseFoo(s);
if (result.ok()) {
result->DoSomething(); // Use -> or *
} else {
LOG(ERROR) << result.status(); // Rich error logging
}
For fallback values, use value_or():
Foo foo = result.value_or(Foo::Default());
Practical Code Examples
Simple Conversion with Error Handling
This example demonstrates the basic pattern for parsing that might fail:
#include "absl/status/statusor.h"
#include "absl/status/status.h"
absl::StatusOr<int> ParseInt(const std::string& s) {
if (s.empty()) return absl::InvalidArgumentError("empty string");
try {
return std::stoi(s); // Implicitly converts to StatusOr<int>
} catch (const std::exception&) {
return absl::InvalidArgumentError("not a number");
}
}
// Caller implementation
void ProcessValue(const std::string& input) {
absl::StatusOr<int> maybe = ParseInt(input);
if (maybe.ok()) {
int v = *maybe; // Dereference like a pointer
Consume(v);
} else {
LOG(ERROR) << maybe.status(); // Access the error status
}
}
Using value_or for Default Values
When you need a default value rather than error handling:
absl::StatusOr<std::string> ReadConfig(const std::string& path) {
if (!FileExists(path)) {
return absl::NotFoundError("config file missing");
}
return ReadFileContents(path);
}
// Caller with fallback
std::string config = ReadConfig("/etc/app.cfg")
.value_or("default_config");
Propagating Errors with ASSIGN_OR_RETURN
For chained operations, use Abseil’s ASSIGN_OR_RETURN macro to automatically return errors up the call stack:
absl::StatusOr<std::string> LoadFile(const std::string& name);
absl::StatusOr<int> ParseHeader(const std::string& data);
absl::StatusOr<User> BuildUser(const std::string& filename) {
// If LoadFile fails, returns the error immediately
ASSIGN_OR_RETURN(std::string data, LoadFile(filename));
ASSIGN_OR_RETURN(int version, ParseHeader(data));
return User{data, version};
}
Working with Move-Only Types
absl::StatusOr<T> fully supports move-only types like std::unique_ptr:
absl::StatusOr<std::unique_ptr<Foo>> MakeFoo() {
if (!ResourceAvailable()) {
return absl::UnavailableError("resource exhausted");
}
return std::make_unique<Foo>();
}
// Caller
absl::StatusOr<std::unique_ptr<Foo>> foo_or = MakeFoo();
if (foo_or.ok()) {
foo_or->DoWork(); // Works with unique_ptr
}
Critical Implementation Details
According to the source code in absl/status/statusor.h, keep these architectural constraints in mind:
- Union semantics:
StatusOr<T>never holds an OK status; success is signaled exclusively by the presence ofT. - Default construction:
StatusOr<T>()creates a non-OK object withStatusCode::kUnknown, meaning!ok()is true initially. - Explicit status constructors: Constructors from a non-OK
absl::Statusare explicit to prevent accidental construction from successful statuses. - Move safety: Moving a
StatusOr<T>that contains an error replaces the source’s status withkInternalto guard against use-after-move scenarios. - Exception throwing: Calling
value()on a non-OKStatusOrthrowsabsl::BadStatusOrAccess, mirroringstd::bad_optional_access.
The implementation in absl/status/statusor.cc and comprehensive tests in absl/status/statusor_test.cc verify these guarantees across all supported types.
Summary
- Replace
std::optional<T>return types withabsl::StatusOr<T>to enable rich error reporting. - Include
absl/status/statusor.handabsl/status/status.hin files performing the migration. - Return success values directly; the class handles implicit conversion from
TtoStatusOr<T>. - Return errors using
absl::Statusfactories likeabsl::InvalidArgumentError()orabsl::NotFoundError(). - Check
ok()before accessing values, then useoperator*oroperator->for pointer-like semantics. - Use
value_or()to provide default values when errors occur. - Reference
absl/status/statusor.hfor the complete API andabsl/status/statusor_test.ccfor usage patterns.
Frequently Asked Questions
What is the difference between absl::StatusOr and std::optional?
std::optional<T> only tracks whether a value is present or absent, providing no information about why a value might be missing. absl::StatusOr<T> carries either a value T or an absl::Status object containing an error code and human-readable message. This makes StatusOr suitable for functions that can fail, while optional is best for values that may simply be missing for legitimate reasons.
How do I construct an error StatusOr?
Return any absl::Status object directly from your function. Abseil provides factory functions like absl::InvalidArgumentError("message"), absl::NotFoundError("message"), and absl::InternalError("message"). The constructor from absl::Status to StatusOr<T> is explicit to prevent accidental construction from OK statuses.
What exception does StatusOr throw on invalid access?
Calling value() on a non-OK StatusOr throws absl::BadStatusOrAccess, which is defined in absl/status/statusor.h. This mirrors the behavior of std::optional::value() throwing std::bad_optional_access. To avoid exceptions, check ok() first or use value_or() with a default.
Can StatusOr hold move-only types like std::unique_ptr?
Yes, absl::StatusOr<T> fully supports move-only types. The implementation properly handles move semantics, allowing you to return std::unique_ptr or other move-only resources. When moving a StatusOr containing an error, the source's status is replaced with kInternal to prevent reuse after move, as implemented in absl/status/statusor.cc.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →