Generic Containers in C: Result Martin Uecker published a design for a generic `result` type in C on 2026-10-24, built as a macro wrapping an algebraic `sum` type that pairs a value with an `error_t` string and carries the `[[nodiscard]]` attribute so compilers warn when a returned result is ignored. The design, part of Uecker's noplate library, provides `result_fail` and `result_success` constructors plus `result_ok`, `result_value` and `result_error` accessors, and Uecker notes the macro-based generics require type arguments to be typedef names. Uecker also observed that Anthropic uses sanitizers to verify generated RISC-V C code, an approach similar to his own work on proving safety properties at compile time with GCC's optimizer and sanitizers. Martin Uecker, 2026-10-24 Previously, I discussed a span type ./2025-07-02.html , a vector type ./2025-07-20.html , an optional type called maybe ./2025-08-10.html , and a variant type variadic ./2025-09-21.html now renamed to variant which makes more sense . I experiment with these types in my noplate library https://codeberg.org/uecker/noplate/ . A couple of months ago, I explored how GCC's optimizer in combination with sanitizer can be mis- used to prove safety properties at compile-time https://uecker.codeberg.page/2026-05-22.html . Interestingly, the idea of using sanitizers for proving safety properties is also used by Anthropic to verify https://www.anthropic.com/research/formal-verification C code, although there it is applied to the generated RISC-V code. Today, I want to discuss the design of a generic result type. We want a type that behaves similar to maybe , but which can not be ignored without a warning and which also encodes an error message. static result int divide int a, int b { if b == -1 && a == INT MIN return result fail int, "overflow in division" ; if b == 0 return result fail int, "division by zero" ; return result success int, a / b ; } We start by defining the result type as a generic macro that simply wraps an algebraic sum type that contains the type of the result and an error type that simply wraps a string. js typedef struct { const char error; } error t; define result T struct nodiscard result T { sum T, error t sum; } Here, we used the nodiscard attribute that tells the compiler to emit a warning when the result is ignored when returned from a function. Side note: A known limitation of our macro-based generics is that the type arguments must be typedef names, but this is only a minor inconvenience which hopefully can be addressed in the future. I still like to make the following observation at this point. An alternative design that uses tagless structures can use arbitrary type names, because it does not need to construct a new tag using token concatenation . Such types then always require a single unique declaration before use. But this has a major problem: Where do you place the declaration for result T and sum T, error T for various types T ? These generic types should be vocabulary types that can freely be used anywhere. Let's continue constructing result by adding two constructors. define result fail T, e result T { sum init T, error t, error t { e } } define result success T, v result T { sum init T, error t, v } And finally also some accessor functions. define result ok T, r r .sum.sel define result value T, r sum car r.sum define result error T, r sum cdr r.sum The sum type is simply a tagged union which works similar as maybe as it traps when the inactive member is accessed. I will write about it another time, as the design of the algebraic types is not yet completed. Now let's look how it works in an example godbolt https://godbolt.org/z/7Y8q8szhP . int main { int a = 2; int b = 1; divide a, b ; } : In function 'main': :34:5: warning: ignoring return value of type 'struct result int', declared with attribute 'nodiscard' -Wunused-result 34 | divide a, b ; | ^~~~~~~~~~~~~~~~~ :17:20: note: in call to 'divide', declared here 17 | static result int divide int a, int b If we ignore the return value, we get the expected warning. Of course, divide is a poor example for this, because ignoring the result is not a problem. For certain other functions this is very useful. Now let's consider a complete example godbolt https://godbolt.org/z/491ec3n4T . int main { int a = 2; int b = 1; result int r = divide a, b ; if result ok int, r { printf "The result is %d.\n", result value int, r ; } else { puts result error int, r .str ; } } But is this now safe? Let's try to use the result value in the false branch. } else { printf "The result is %d.\n", result value int, r ; puts result error int, r .str ; } If you also change b to zero, this will trigger a run-time error. But what if I want to avoid run-time errors? Let's try our previously described trick https://uecker.codeberg.page/2026-05-22.html of re-declaring builtin trap with the warning "UNSAFE" attribute. It works Try it yourself on godbolt https://godbolt.org/z/rzYGWMoWK by changing the if 0 to if 1 . This works because because noplate internally also uses builtin trap . There is still a minor potential issue with the design of the type. If we add gnu::noinline,gnu::noipa to the divide function, so that the compiler will not look into it, it will then complain that the argument to puts maybe null. This is actually nice, as it tells us that there might be a run-time issue Note, that in this example, without the attributes, it can see that the pointers to the error strings are not null, but when the divide function were moved into a library, the information about the pointers would be inaccessible to the optimizer. To avoid this, we could modify the error t type to ensure that the error string is always initialized in a way the optimizer can see this. For example, a version which uses the strv string view type from the noplate library instead of a traditional C string does not have this problem the noplate's README contains a link to such an example . For now, we can just add a check for this. The complete example, that the compiler now has proven statically safe with regard to signed integer operations, null pointers, and error handling can be found here godbolt https://godbolt.org/z/YKfnYdqb7 . Of course, the compile-time check using GCC's optimizer is hack, but it shows what can be done relatively easily with existing technology already shipped with compilers. We only need a better interface to it. There is a proposal to add a compiler assert feature to C PDF https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3950.pdf , but WG14 was not yet convinced.