Result Library 0.1
Elegant error handling in C
Loading...
Searching...
No Matches
Result Library

Introduction

Wave goodbye to output parameters, sentinel values, and endless null checking! Embrace clean, efficient error handling today by encapsulating operations that may succeed or fail in a type-safe way.

  • Boost Performance Fewer pointer dereferences; rely on optimized single return values
  • Simple API Handle success and failure scenarios with just a handful of C macros
  • Streamlined Error Handling Reduce the chances of incorrect or inconsistent error handling
  • Safe Execution Enforce valid states, eliminating risks like dangling pointers or stale data
  • Enhanced Readability Reduce boilerplate code to make your code easier to understand
  • Functional Style Avoid side effects and encourage pure, composable functions
  • Lightweight Keep your project slim with no extra dependencies
  • Open Source Enjoy transparent, permissive Apache 2 licensing
  • Header-Only C Library Compatible with C23 standard and modern compilers
Remarks
This library provides a cleaner, safer, and more modern approach to error handling by combining function result and error into a unified return value. It enforces correct usage at the call site, reduces the risk of bugs, and leads to more maintainable and extensible code.

TL;DR

Not a fan of reading long docs? No worries! Tune in to Deep Dive, a podcast generated by NetbookLM. In just a few minutes, you'll get the essential details and a fun intro to what this library can do for you!

Deep Dive Podcast
Deep Dive into the Result Library

Results in a Nutshell

Result objects represent the outcome of an operation, removing the need for output parameters, sentinel values, and null checking. Operations that succeed produce results encapsulating a success value; operations that fail produce results with a failure value. Success and failure can be represented by whatever types make the most sense for each operation.

Let's use a pet store example to show how this library can simplify your code.

Start by defining some data types to represent pets.

// Pet status in the store
typedef enum pet_status { AVAILABLE, PENDING, SOLD } pet_status;
// Represents a pet
typedef struct pet { int id; const char * name; pet_status status; } * Pet;
// Convenience macros
#define PET_ID(pet) (pet)->id
#define PET_NAME(pet) (pet)->name
#define PET_STATUS(pet) (pet)->status
// Pet error codes
typedef enum pet_error { OK, PET_NOT_FOUND, PET_NOT_AVAILABLE, PET_ALREADY_SOLD } pet_error;

Next, let's say you have an array of pets acting as your "database".

// Available pets in the store
static struct pet pets[] = {
{ .id = 0, .name = "Rocky", .status = AVAILABLE },
{ .id = 1, .name = "Garfield", .status = PENDING },
{ .id = 2, .name = "Rantanplan", .status = SOLD }
};
Note
You'll use a function, find_pet(id), to retrieve pets by their ID. We'll skip its implementation for now.

Suppose you need to write a function to get a pet's status.

Warning
This works... until someone passes an invalid ID. If find_pet returns NULL, your code could crash unexpectedly.

To fix this, you refactor the function to return an error code and use a pointer to "return" the status.

It's safer, but also clunky.

Warning
What if the pointer is NULL? Should you return a new error code? Use assert? It's starting to feel messy.

Instead of juggling pointers and error codes, you should return a Result object.

Remarks
The result encapsulates both success (pet status) and failure (error code) in one clean package. The caller immediately knows the function can fail, and you've eliminated the need for a pointer. This is simpler, safer, and much easier to read.

Encouraged, you refactor find_pet to return a failed result instead of NULL when a pet isn't found. Now, get_pet_status can rely on find_pet to handle errors and focus on the happy path.

Note
And just like that, your code becomes robust and maintainable. No more mysterious crashes, no awkward pointer checks. Just clean, elegant error handling.

With Results, handling success and failure feels natural, leaving you free to focus on the fun parts of coding.

Getting Started

Adding Results to Your Project

This library consists of one header file only. All you need to do is copy result.h into your project, and include it.

#include <result.h>
A header-only C library to handle errors elegantly.

Since it's a header-only library, there is no library code to link against.

Result Types and Variables

  • RESULT_STRUCT Declares a result struct with a default tag and the supplied success and failure types.
    RESULT_STRUCT(pet_status, pet_error);
  • RESULT Returns the type specifier for results with the supplied success and failure type names.
    RESULT(pet_status, pet_error) result;

Creating Result Objects

Basic Usage

Checking Success or Failure

Unwrapping Values

Conditional Actions

Advanced Usage

Screening Results

Transforming Values

Additional Info

Compatibility

Results rely on modern C features such as designated initializers, compound literals, and typeof.

Releases

This library adheres to Semantic Versioning. All notable changes for each version are documented in a change log.

Head over to GitHub for the latest release.

Latest Release

Source Code

The source code is available on GitHub.

Fork me on GitHub