1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* A type suitable for returning either a value or an error from a function. */
9 #ifndef mozilla_Result_h
10 #define mozilla_Result_h
12 #include <type_traits>
13 #include "mozilla/Alignment.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/Types.h"
17 #include "mozilla/Variant.h"
22 * Empty struct, indicating success for operations that have no return value.
23 * For example, if you declare another empty struct `struct OutOfMemory {};`,
24 * then `Result<Ok, OutOfMemory>` represents either success or OOM.
29 class GenericErrorResult
;
30 template <typename V
, typename E
>
35 enum class PackingStrategy
{
42 template <typename V
, typename E
, PackingStrategy Strategy
>
43 class ResultImplementation
;
45 template <typename V
, typename E
>
46 class ResultImplementation
<V
, E
, PackingStrategy::Variant
> {
47 mozilla::Variant
<V
, E
> mStorage
;
50 ResultImplementation(ResultImplementation
&&) = default;
51 ResultImplementation(const ResultImplementation
&) = default;
52 ResultImplementation
& operator=(const ResultImplementation
&) = default;
53 ResultImplementation
& operator=(ResultImplementation
&&) = default;
55 explicit ResultImplementation(V
&& aValue
)
56 : mStorage(std::forward
<V
>(aValue
)) {}
57 explicit ResultImplementation(const V
& aValue
) : mStorage(aValue
) {}
58 explicit ResultImplementation(const E
& aErrorValue
) : mStorage(aErrorValue
) {}
59 explicit ResultImplementation(E
&& aErrorValue
)
60 : mStorage(std::forward
<E
>(aErrorValue
)) {}
62 bool isOk() const { return mStorage
.template is
<V
>(); }
64 // The callers of these functions will assert isOk() has the proper value, so
65 // these functions (in all ResultImplementation specializations) don't need
67 V
unwrap() { return std::move(mStorage
.template as
<V
>()); }
68 const V
& inspect() const { return mStorage
.template as
<V
>(); }
70 E
unwrapErr() { return std::move(mStorage
.template as
<E
>()); }
71 const E
& inspectErr() const { return mStorage
.template as
<E
>(); }
75 * mozilla::Variant doesn't like storing a reference. This is a specialization
76 * to store E as pointer if it's a reference.
78 template <typename V
, typename E
>
79 class ResultImplementation
<V
, E
&, PackingStrategy::Variant
> {
80 mozilla::Variant
<V
, E
*> mStorage
;
83 explicit ResultImplementation(V
&& aValue
)
84 : mStorage(std::forward
<V
>(aValue
)) {}
85 explicit ResultImplementation(const V
& aValue
) : mStorage(aValue
) {}
86 explicit ResultImplementation(E
& aErrorValue
) : mStorage(&aErrorValue
) {}
88 bool isOk() const { return mStorage
.template is
<V
>(); }
90 const V
& inspect() const { return mStorage
.template as
<V
>(); }
91 V
unwrap() { return std::move(mStorage
.template as
<V
>()); }
93 E
& unwrapErr() { return *mStorage
.template as
<E
*>(); }
94 const E
& inspectErr() const { return *mStorage
.template as
<E
*>(); }
98 * Specialization for when the success type is Ok (or another empty class) and
99 * the error type is a reference.
101 template <typename V
, typename E
>
102 class ResultImplementation
<V
, E
&, PackingStrategy::NullIsOk
> {
106 explicit ResultImplementation(V
) : mErrorValue(nullptr) {}
107 explicit ResultImplementation(E
& aErrorValue
) : mErrorValue(&aErrorValue
) {}
109 bool isOk() const { return mErrorValue
== nullptr; }
111 const V
& inspect() const = delete;
112 V
unwrap() { return V(); }
114 const E
& inspectErr() const { return *mErrorValue
; }
115 E
& unwrapErr() { return *mErrorValue
; }
119 * Specialization for when the success type is Ok (or another empty class) and
120 * the error type is a value type which can never have the value 0 (as
121 * determined by UnusedZero<>).
123 template <typename V
, typename E
>
124 class ResultImplementation
<V
, E
, PackingStrategy::NullIsOk
> {
125 static constexpr E NullValue
= E(0);
130 explicit ResultImplementation(V
) : mErrorValue(NullValue
) {}
131 explicit ResultImplementation(E aErrorValue
) : mErrorValue(aErrorValue
) {
132 MOZ_ASSERT(aErrorValue
!= NullValue
);
135 bool isOk() const { return mErrorValue
== NullValue
; }
137 const V
& inspect() const = delete;
138 V
unwrap() { return V(); }
140 const E
& inspectErr() const { return mErrorValue
; }
141 E
unwrapErr() { return std::move(mErrorValue
); }
145 * Specialization for when alignment permits using the least significant bit as
148 template <typename V
, typename E
>
149 class ResultImplementation
<V
*, E
&, PackingStrategy::LowBitTagIsError
> {
153 explicit ResultImplementation(V
* aValue
)
154 : mBits(reinterpret_cast<uintptr_t>(aValue
)) {
155 MOZ_ASSERT((uintptr_t(aValue
) % MOZ_ALIGNOF(V
)) == 0,
156 "Result value pointers must not be misaligned");
158 explicit ResultImplementation(E
& aErrorValue
)
159 : mBits(reinterpret_cast<uintptr_t>(&aErrorValue
) | 1) {
160 MOZ_ASSERT((uintptr_t(&aErrorValue
) % MOZ_ALIGNOF(E
)) == 0,
161 "Result errors must not be misaligned");
164 bool isOk() const { return (mBits
& 1) == 0; }
166 V
* inspect() const { return reinterpret_cast<V
*>(mBits
); }
167 V
* unwrap() { return inspect(); }
169 E
& inspectErr() const { return *reinterpret_cast<E
*>(mBits
^ 1); }
170 E
& unwrapErr() { return inspectErr(); }
173 // Return true if any of the struct can fit in a word.
174 template <typename V
, typename E
>
175 struct IsPackableVariant
{
188 std::conditional_t
<sizeof(VEbool
) <= sizeof(EVbool
), VEbool
, EVbool
>;
190 static const bool value
= sizeof(Impl
) <= sizeof(uintptr_t);
194 * Specialization for when both type are not using all the bytes, in order to
195 * use one byte as a tag.
197 template <typename V
, typename E
>
198 class ResultImplementation
<V
, E
, PackingStrategy::PackedVariant
> {
199 using Impl
= typename IsPackableVariant
<V
, E
>::Impl
;
203 explicit ResultImplementation(V aValue
) {
204 data
.v
= std::move(aValue
);
207 explicit ResultImplementation(E aErrorValue
) {
208 data
.e
= std::move(aErrorValue
);
212 bool isOk() const { return data
.ok
; }
214 const V
& inspect() const { return data
.v
; }
215 V
unwrap() { return std::move(data
.v
); }
217 const E
& inspectErr() const { return data
.e
; }
218 E
unwrapErr() { return std::move(data
.e
); }
221 // To use nullptr as a special value, we need the counter part to exclude zero
222 // from its range of valid representations.
224 // By default assume that zero can be represented.
225 template <typename T
>
227 static const bool value
= false;
230 // References can't be null.
231 template <typename T
>
232 struct UnusedZero
<T
&> {
233 static const bool value
= true;
236 // A bit of help figuring out which of the above specializations to use.
238 // We begin by safely assuming types don't have a spare bit.
239 template <typename T
>
241 static const bool value
= false;
244 // As an incomplete type, void* does not have a spare bit.
246 struct HasFreeLSB
<void*> {
247 static const bool value
= false;
250 // The lowest bit of a properly-aligned pointer is always zero if the pointee
251 // type is greater than byte-aligned. That bit is free to use if it's masked
252 // out of such pointers before they're dereferenced.
253 template <typename T
>
254 struct HasFreeLSB
<T
*> {
255 static const bool value
= (alignof(T
) & 1) == 0;
258 // We store references as pointers, so they have a free bit if a pointer would
260 template <typename T
>
261 struct HasFreeLSB
<T
&> {
262 static const bool value
= HasFreeLSB
<T
*>::value
;
265 // Select one of the previous result implementation based on the properties of
266 // the V and E types.
267 template <typename V
, typename E
>
268 struct SelectResultImpl
{
269 static const PackingStrategy value
=
270 (std::is_empty_v
<V
> && UnusedZero
<E
>::value
)
271 ? PackingStrategy::NullIsOk
272 : (detail::HasFreeLSB
<V
>::value
&& detail::HasFreeLSB
<E
>::value
)
273 ? PackingStrategy::LowBitTagIsError
274 : (std::is_default_constructible_v
<V
> &&
275 std::is_default_constructible_v
<E
> &&
276 IsPackableVariant
<V
, E
>::value
)
277 ? PackingStrategy::PackedVariant
278 : PackingStrategy::Variant
;
280 using Type
= detail::ResultImplementation
<V
, E
, value
>;
283 template <typename T
>
284 struct IsResult
: std::false_type
{};
286 template <typename V
, typename E
>
287 struct IsResult
<Result
<V
, E
>> : std::true_type
{};
289 } // namespace detail
291 template <typename V
, typename E
>
292 auto ToResult(Result
<V
, E
>&& aValue
)
293 -> decltype(std::forward
<Result
<V
, E
>>(aValue
)) {
294 return std::forward
<Result
<V
, E
>>(aValue
);
298 * Result<V, E> represents the outcome of an operation that can either succeed
299 * or fail. It contains either a success value of type V or an error value of
302 * All Result methods are const, so results are basically immutable.
303 * This is just like Variant<V, E> but with a slightly different API, and the
304 * following cases are optimized so Result can be stored more efficiently:
306 * - If the success type is Ok (or another empty class) and the error type is a
307 * reference, Result<V, E&> is guaranteed to be pointer-sized and all zero
308 * bits on success. Do not change this representation! There is JIT code that
311 * - If the success type is a pointer type and the error type is a reference
312 * type, and the least significant bit is unused for both types when stored
313 * as a pointer (due to alignment rules), Result<V*, E&> is guaranteed to be
314 * pointer-sized. In this case, we use the lowest bit as tag bit: 0 to
315 * indicate the Result's bits are a V, 1 to indicate the Result's bits (with
316 * the 1 masked out) encode an E*.
318 * The purpose of Result is to reduce the screwups caused by using `false` or
319 * `nullptr` to indicate errors.
320 * What screwups? See <https://bugzilla.mozilla.org/show_bug.cgi?id=912928> for
323 template <typename V
, typename E
>
324 class MOZ_MUST_USE_TYPE Result final
{
325 using Impl
= typename
detail::SelectResultImpl
<V
, E
>::Type
;
333 /** Create a success result. */
334 MOZ_IMPLICIT
Result(V
&& aValue
) : mImpl(std::forward
<V
>(aValue
)) {
338 /** Create a success result. */
339 MOZ_IMPLICIT
Result(const V
& aValue
) : mImpl(aValue
) { MOZ_ASSERT(isOk()); }
341 /** Create an error result. */
342 explicit Result(E aErrorValue
) : mImpl(std::forward
<E
>(aErrorValue
)) {
347 * Implementation detail of MOZ_TRY().
348 * Create an error result from another error result.
350 template <typename E2
>
351 MOZ_IMPLICIT
Result(GenericErrorResult
<E2
>&& aErrorResult
)
352 : mImpl(std::forward
<E2
>(aErrorResult
.mErrorValue
)) {
353 static_assert(std::is_convertible_v
<E2
, E
>, "E2 must be convertible to E");
358 * Implementation detail of MOZ_TRY().
359 * Create an error result from another error result.
361 template <typename E2
>
362 MOZ_IMPLICIT
Result(const GenericErrorResult
<E2
>& aErrorResult
)
363 : mImpl(aErrorResult
.mErrorValue
) {
364 static_assert(std::is_convertible_v
<E2
, E
>, "E2 must be convertible to E");
368 Result(const Result
&) = default;
369 Result(Result
&&) = default;
370 Result
& operator=(const Result
&) = default;
371 Result
& operator=(Result
&&) = default;
373 /** True if this Result is a success result. */
374 bool isOk() const { return mImpl
.isOk(); }
376 /** True if this Result is an error result. */
377 bool isErr() const { return !mImpl
.isOk(); }
379 /** Take the success value from this Result, which must be a success result.
383 return mImpl
.unwrap();
387 * Take the success value from this Result, which must be a success result.
388 * If it is an error result, then return the aValue.
390 V
unwrapOr(V aValue
) {
391 return MOZ_LIKELY(isOk()) ? mImpl
.unwrap() : std::move(aValue
);
394 /** Take the error value from this Result, which must be an error result. */
397 return mImpl
.unwrapErr();
400 /** See the success value from this Result, which must be a success result. */
401 const V
& inspect() const { return mImpl
.inspect(); }
403 /** See the error value from this Result, which must be an error result. */
404 const E
& inspectErr() const {
406 return mImpl
.inspectErr();
409 /** Propagate the error value from this Result, which must be an error result.
411 * This can be used to propagate an error from a function call to the caller
412 * with a different value type, but the same error type:
414 * Result<T1, E> Func1() {
415 * Result<T2, E> res = Func2();
416 * if (res.isErr()) { return res.propagateErr(); }
419 GenericErrorResult
<E
> propagateErr() {
421 return GenericErrorResult
<E
>{mImpl
.unwrapErr()};
425 * Map a function V -> W over this result's success variant. If this result is
426 * an error, do not invoke the function and propagate the error.
428 * Mapping over success values invokes the function to produce a new success
431 * // Map Result<int, E> to another Result<int, E>
432 * Result<int, E> res(5);
433 * Result<int, E> res2 = res.map([](int x) { return x * x; });
434 * MOZ_ASSERT(res2.unwrap() == 25);
436 * // Map Result<const char*, E> to Result<size_t, E>
437 * Result<const char*, E> res("hello, map!");
438 * Result<size_t, E> res2 = res.map(strlen);
439 * MOZ_ASSERT(res2.unwrap() == 11);
441 * Mapping over an error does not invoke the function and propagates the
444 * Result<V, int> res(5);
445 * MOZ_ASSERT(res.isErr());
446 * Result<W, int> res2 = res.map([](V v) { ... });
447 * MOZ_ASSERT(res2.isErr());
448 * MOZ_ASSERT(res2.unwrapErr() == 5);
450 template <typename F
>
451 auto map(F f
) -> Result
<decltype(f(*((V
*)nullptr))), E
> {
452 using RetResult
= Result
<decltype(f(*((V
*)nullptr))), E
>;
453 return MOZ_LIKELY(isOk()) ? RetResult(f(unwrap())) : RetResult(unwrapErr());
457 * Map a function V -> W over this result's error variant. If this result is
458 * a success, do not invoke the function and move the success over.
460 * Mapping over error values invokes the function to produce a new error
463 * // Map Result<V, int> to another Result<V, int>
464 * Result<V, int> res(5);
465 * Result<V, int> res2 = res.mapErr([](int x) { return x * x; });
466 * MOZ_ASSERT(res2.unwrapErr() == 25);
468 * // Map Result<V, const char*> to Result<V, size_t>
469 * Result<V, const char*> res("hello, map!");
470 * Result<size_t, E> res2 = res.mapErr(strlen);
471 * MOZ_ASSERT(res2.unwrapErr() == 11);
473 * Mapping over a success does not invoke the function and copies the error:
475 * Result<int, V> res(5);
476 * MOZ_ASSERT(res.isOk());
477 * Result<int, W> res2 = res.mapErr([](V v) { ... });
478 * MOZ_ASSERT(res2.isOk());
479 * MOZ_ASSERT(res2.unwrap() == 5);
481 template <typename F
>
482 auto mapErr(F f
) -> Result
<V
, std::result_of_t
<F(E
)>> {
483 using RetResult
= Result
<V
, std::result_of_t
<F(E
)>>;
484 return isOk() ? RetResult(unwrap()) : RetResult(f(unwrapErr()));
488 * Given a function V -> Result<W, E>, apply it to this result's success value
489 * and return its result. If this result is an error value, it is propagated.
491 * This is sometimes called "flatMap" or ">>=" in other contexts.
493 * `andThen`ing over success values invokes the function to produce a new
496 * Result<const char*, Error> res("hello, andThen!");
497 * Result<HtmlFreeString, Error> res2 = res.andThen([](const char* s) {
498 * return containsHtmlTag(s)
499 * ? Result<HtmlFreeString, Error>(Error("Invalid: contains HTML"))
500 * : Result<HtmlFreeString, Error>(HtmlFreeString(s));
503 * MOZ_ASSERT(res2.isOk());
504 * MOZ_ASSERT(res2.unwrap() == HtmlFreeString("hello, andThen!");
506 * `andThen`ing over error results does not invoke the function, and just
507 * propagates the error result:
509 * Result<int, const char*> res("some error");
510 * auto res2 = res.andThen([](int x) { ... });
511 * MOZ_ASSERT(res2.isErr());
512 * MOZ_ASSERT(res.unwrapErr() == res2.unwrapErr());
514 template <typename F
, typename
= std::enable_if_t
<detail::IsResult
<
515 decltype((*((F
*)nullptr))(*((V
*)nullptr)))>::value
>>
516 auto andThen(F f
) -> decltype(f(*((V
*)nullptr))) {
517 return MOZ_LIKELY(isOk()) ? f(unwrap()) : propagateErr();
522 * A type that auto-converts to an error Result. This is like a Result without
523 * a success type. It's the best return type for functions that always return
524 * an error--functions designed to build and populate error objects. It's also
525 * useful in error-handling macros; see MOZ_TRY for an example.
527 template <typename E
>
528 class MOZ_MUST_USE_TYPE GenericErrorResult
{
531 template <typename V
, typename E2
>
535 explicit GenericErrorResult(E
&& aErrorValue
)
536 : mErrorValue(std::forward
<E
>(aErrorValue
)) {}
539 template <typename E
>
540 inline GenericErrorResult
<E
> Err(E
&& aErrorValue
) {
541 return GenericErrorResult
<E
>(std::forward
<E
>(aErrorValue
));
544 } // namespace mozilla
547 * MOZ_TRY(expr) is the C++ equivalent of Rust's `try!(expr);`. First, it
548 * evaluates expr, which must produce a Result value. On success, it
549 * discards the result altogether. On error, it immediately returns an error
550 * Result from the enclosing function.
552 #define MOZ_TRY(expr) \
554 auto mozTryTempResult_ = ::mozilla::ToResult(expr); \
555 if (MOZ_UNLIKELY(mozTryTempResult_.isErr())) { \
556 return mozTryTempResult_.propagateErr(); \
561 * MOZ_TRY_VAR(target, expr) is the C++ equivalent of Rust's `target =
562 * try!(expr);`. First, it evaluates expr, which must produce a Result value. On
563 * success, the result's success value is assigned to target. On error,
564 * immediately returns the error result. |target| must evaluate to a reference
565 * without any side effects.
567 #define MOZ_TRY_VAR(target, expr) \
569 auto mozTryVarTempResult_ = (expr); \
570 if (MOZ_UNLIKELY(mozTryVarTempResult_.isErr())) { \
571 return mozTryVarTempResult_.propagateErr(); \
573 (target) = mozTryVarTempResult_.unwrap(); \
576 #endif // mozilla_Result_h