Skip to content

Instantly share code, notes, and snippets.

@emrebayrm
Created October 6, 2025 15:09
Show Gist options
  • Save emrebayrm/ea895f850afaa43c7afa0ea110c631eb to your computer and use it in GitHub Desktop.
Save emrebayrm/ea895f850afaa43c7afa0ea110c631eb to your computer and use it in GitHub Desktop.
Rust like Result object and the helpers.
#pragma once
#include <optional>
#include <string>
#include <variant>
/// Ok object with value
template <typename T>
struct Ok
{
/// T type okay value
T value;
};
/// void return, Ok type
template <>
struct Ok<void>
{
};
/// Error type with custom defined error type E
template <typename E>
struct Err
{
/// Err object creation with error value
/// @param e error object
explicit Err(const E &e)
: err(e) {}
/// error data
E err;
};
/// BadAccess exception
struct BadAccess : std::exception
{
};
/// @brief Result object that holds either a value of type T or an error of type E.
/// @tparam T The type of the expected value.
/// @tparam E The type of the error.
template <typename T, typename E>
class Result
{
public:
/// @brief Constructs a Result with a successful value.
/// @param ok The successful result wrapper containing the value.
Result(const Ok<T> &ok)
: res(ok) {}
/// @brief Constructs a Result with an error.
/// @param err The error wrapper containing the error.
Result(const Err<E> &err)
: res(err) {}
/// @brief Checks if the result is successful.
/// @return True if the result holds a successful value, false otherwise.
explicit operator bool() const
{
return this->isOk();
}
/// @brief Checks if the result holds a successful value.
/// @return True if the result is an Ok<T>, false otherwise.
bool isOk() const
{
return std::holds_alternative<Ok<T>>(res);
}
/// @brief Checks if the result holds an error.
/// @return True if the result is an Err<E>, false otherwise.
bool isError() const
{
return std::holds_alternative<Err<E>>(res);
}
/// @brief Retrieves the error if the result is an error.
/// @throws BadAccess if the result holds a successful value.
/// @return A constant reference to the error.
const E &error() const
{
if (!this->isOk())
{
return std::get<Err<E>>(res).err;
}
throw BadAccess{};
}
/// @brief Retrieves the value if the result is successful.
/// @throws BadAccess if the result holds an error.
/// @return A constant reference to the value.
const T &value() const
{
if (this->isOk())
{
return std::get<Ok<T>>(res).value;
}
throw BadAccess{};
}
private:
/// @brief Internal variant holding either Ok<T> or Err<E>.
std::variant<Ok<T>, Err<E>> res;
};
/// @brief Specialized Result class for cases where there is no success value (void) but an error is possible.
/// @tparam E The type of the error.
template <typename E>
class Result<void, E>
{
public:
/// @brief Constructs a successful Result (no error).
Result()
: res(true)
, error_value(std::nullopt) {}
/// @brief Constructs a Result with an error.
/// @param err The error wrapper containing the error.
Result(const Err<E> &err)
: res(false)
, error_value(err) {}
/// @brief Checks if the result is successful.
/// @return True if successful, false if there is an error.
explicit operator bool() const
{
return res;
}
/// @brief Retrieves the error if the result is an error.
/// @throws BadAccess if the result is successful.
/// @return A constant reference to the error.
const E &error() const
{
if (!res && error_value.has_value())
{
return error_value->err;
}
throw BadAccess{};
}
private:
/// @brief True if successful, false if an error occurred.
bool res;
/// @brief Holds the error value when the result is an error.
std::optional<Err<E>> error_value;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment