Bug 1659660 - Improve documentation and testing for mozilla::Result::map and mozilla::Result::mapErr; r=froydnj,dom-workers-and-storage-reviewers,sg

Differential Revision: https://phabricator.services.mozilla.com/D87414
This commit is contained in:
Jan Varga 2020-08-20 11:04:12 +00:00
parent 8561a6fd69
commit d749be0c59
2 changed files with 130 additions and 38 deletions

View File

@ -422,8 +422,8 @@ class MOZ_MUST_USE_TYPE Result final {
}
/**
* Map a function V -> W over this result's success variant. If this result is
* an error, do not invoke the function and propagate the error.
* Map a function V -> V2 over this result's success variant. If this result
* is an error, do not invoke the function and propagate the error.
*
* Mapping over success values invokes the function to produce a new success
* value:
@ -431,11 +431,13 @@ class MOZ_MUST_USE_TYPE Result final {
* // Map Result<int, E> to another Result<int, E>
* Result<int, E> res(5);
* Result<int, E> res2 = res.map([](int x) { return x * x; });
* MOZ_ASSERT(res.isOk());
* MOZ_ASSERT(res2.unwrap() == 25);
*
* // Map Result<const char*, E> to Result<size_t, E>
* Result<const char*, E> res("hello, map!");
* Result<size_t, E> res2 = res.map(strlen);
* MOZ_ASSERT(res.isOk());
* MOZ_ASSERT(res2.unwrap() == 11);
*
* Mapping over an error does not invoke the function and propagates the
@ -443,7 +445,7 @@ class MOZ_MUST_USE_TYPE Result final {
*
* Result<V, int> res(5);
* MOZ_ASSERT(res.isErr());
* Result<W, int> res2 = res.map([](V v) { ... });
* Result<V2, int> res2 = res.map([](V v) { ... });
* MOZ_ASSERT(res2.isErr());
* MOZ_ASSERT(res2.unwrapErr() == 5);
*/
@ -454,7 +456,7 @@ class MOZ_MUST_USE_TYPE Result final {
}
/**
* Map a function V -> W over this result's error variant. If this result is
* Map a function E -> E2 over this result's error variant. If this result is
* a success, do not invoke the function and move the success over.
*
* Mapping over error values invokes the function to produce a new error
@ -463,30 +465,34 @@ class MOZ_MUST_USE_TYPE Result final {
* // Map Result<V, int> to another Result<V, int>
* Result<V, int> res(5);
* Result<V, int> res2 = res.mapErr([](int x) { return x * x; });
* MOZ_ASSERT(res2.isErr());
* MOZ_ASSERT(res2.unwrapErr() == 25);
*
* // Map Result<V, const char*> to Result<V, size_t>
* Result<V, const char*> res("hello, map!");
* Result<size_t, E> res2 = res.mapErr(strlen);
* MOZ_ASSERT(res2.unwrapErr() == 11);
* Result<V, const char*> res("hello, mapErr!");
* Result<V, size_t> res2 = res.mapErr(strlen);
* MOZ_ASSERT(res2.isErr());
* MOZ_ASSERT(res2.unwrapErr() == 14);
*
* Mapping over a success does not invoke the function and copies the error:
* Mapping over a success does not invoke the function and moves the success:
*
* Result<int, V> res(5);
* Result<int, E> res(5);
* MOZ_ASSERT(res.isOk());
* Result<int, W> res2 = res.mapErr([](V v) { ... });
* Result<int, E2> res2 = res.mapErr([](E e) { ... });
* MOZ_ASSERT(res2.isOk());
* MOZ_ASSERT(res2.unwrap() == 5);
*/
template <typename F>
auto mapErr(F f) -> Result<V, std::result_of_t<F(E)>> {
using RetResult = Result<V, std::result_of_t<F(E)>>;
return isOk() ? RetResult(unwrap()) : RetResult(f(unwrapErr()));
return MOZ_UNLIKELY(isErr()) ? RetResult(f(unwrapErr()))
: RetResult(unwrap());
}
/**
* Given a function V -> Result<W, E>, apply it to this result's success value
* and return its result. If this result is an error value, it is propagated.
* Given a function V -> Result<V2, E>, apply it to this result's success
* value and return its result. If this result is an error value, it is
* propagated.
*
* This is sometimes called "flatMap" or ">>=" in other contexts.
*

View File

@ -178,34 +178,119 @@ static void MapTest() {
explicit MyError(int y) : x(y) {}
};
// Mapping over success values.
Result<int, MyError> res(5);
bool invoked = false;
auto res2 = res.map([&invoked](int x) {
MOZ_RELEASE_ASSERT(x == 5);
invoked = true;
return "hello";
});
MOZ_RELEASE_ASSERT(res2.isOk());
MOZ_RELEASE_ASSERT(invoked);
MOZ_RELEASE_ASSERT(strcmp(res2.unwrap(), "hello") == 0);
// Mapping over success values, to the same success type.
{
Result<int, MyError> res(5);
bool invoked = false;
auto res2 = res.map([&invoked](int x) {
MOZ_RELEASE_ASSERT(x == 5);
invoked = true;
return 6;
});
MOZ_RELEASE_ASSERT(res2.isOk());
MOZ_RELEASE_ASSERT(invoked);
MOZ_RELEASE_ASSERT(res2.unwrap() == 6);
}
// Mapping over success values, to a different success type.
{
Result<int, MyError> res(5);
bool invoked = false;
auto res2 = res.map([&invoked](int x) {
MOZ_RELEASE_ASSERT(x == 5);
invoked = true;
return "hello";
});
MOZ_RELEASE_ASSERT(res2.isOk());
MOZ_RELEASE_ASSERT(invoked);
MOZ_RELEASE_ASSERT(strcmp(res2.unwrap(), "hello") == 0);
}
// Mapping over error values.
MyError err(1);
Result<char, MyError> res3(err);
MOZ_RELEASE_ASSERT(res3.isErr());
Result<char, MyError> res4 = res3.map([](int x) {
MOZ_RELEASE_ASSERT(false);
return 'a';
});
MOZ_RELEASE_ASSERT(res4.isErr());
MOZ_RELEASE_ASSERT(res4.unwrapErr().x == err.x);
{
MyError err(1);
Result<char, MyError> res(err);
MOZ_RELEASE_ASSERT(res.isErr());
Result<char, MyError> res2 = res.map([](int x) {
MOZ_RELEASE_ASSERT(false);
return 'a';
});
MOZ_RELEASE_ASSERT(res2.isErr());
MOZ_RELEASE_ASSERT(res2.unwrapErr().x == err.x);
}
// Function pointers instead of lamdbas as the mapping function.
Result<const char*, MyError> res5("hello");
auto res6 = res5.map(strlen);
MOZ_RELEASE_ASSERT(res6.isOk());
MOZ_RELEASE_ASSERT(res6.unwrap() == 5);
// Function pointers instead of lambdas as the mapping function.
{
Result<const char*, MyError> res("hello");
auto res2 = res.map(strlen);
MOZ_RELEASE_ASSERT(res2.isOk());
MOZ_RELEASE_ASSERT(res2.unwrap() == 5);
}
}
static void MapErrTest() {
struct MyError {
int x;
explicit MyError(int y) : x(y) {}
};
struct MyError2 {
int a;
explicit MyError2(int b) : a(b) {}
};
// Mapping over error values, to the same error type.
{
MyError err(1);
Result<char, MyError> res(err);
MOZ_RELEASE_ASSERT(res.isErr());
bool invoked = false;
auto res2 = res.mapErr([&invoked](const auto err) {
MOZ_RELEASE_ASSERT(err.x == 1);
invoked = true;
return MyError(2);
});
MOZ_RELEASE_ASSERT(res2.isErr());
MOZ_RELEASE_ASSERT(invoked);
MOZ_RELEASE_ASSERT(res2.unwrapErr().x == 2);
}
// Mapping over error values, to a different error type.
{
MyError err(1);
Result<char, MyError> res(err);
MOZ_RELEASE_ASSERT(res.isErr());
bool invoked = false;
auto res2 = res.mapErr([&invoked](const auto err) {
MOZ_RELEASE_ASSERT(err.x == 1);
invoked = true;
return MyError2(2);
});
MOZ_RELEASE_ASSERT(res2.isErr());
MOZ_RELEASE_ASSERT(invoked);
MOZ_RELEASE_ASSERT(res2.unwrapErr().a == 2);
}
// Mapping over success values.
{
Result<int, MyError> res(5);
auto res2 = res.mapErr([](const auto err) {
MOZ_RELEASE_ASSERT(false);
return MyError(1);
});
MOZ_RELEASE_ASSERT(res2.isOk());
MOZ_RELEASE_ASSERT(res2.unwrap() == 5);
}
// Function pointers instead of lambdas as the mapping function.
{
Result<Ok, const char*> res("hello");
auto res2 = res.mapErr(strlen);
MOZ_RELEASE_ASSERT(res2.isErr());
MOZ_RELEASE_ASSERT(res2.unwrapErr() == 5);
}
}
static void AndThenTest() {
@ -298,6 +383,7 @@ int main() {
EmptyValueTest();
ReferenceTest();
MapTest();
MapErrTest();
AndThenTest();
UniquePtrTest();
return 0;