Share
Embed
Transcript
35 slides
-
RustのResult<T, E> Rustのエラーハンドリングで使われる型 成功を表し値を含むOk(T) およびエラーを表しエラー値を含むErr(E) の列挙型(代数的データ型)
正常値も異常値も両方運搬できる エラーメッセージも運搬できる -
Mitama.Result v2 https://github.com/LoliGothick/mitama-cpp-result Resultの主要なAPIをほとんど再現したC++17のライブラリ サポートコンパイラは gcc-8.3
gcc-9.1 clang-7 clang-8 依存ライブラリ(Boost) Hana, Variant, Optional, Format -
クラス宣言 enum class mutability: bool { mut = false, immut
= true, }; template <mutability, // mutability specification class = std::monostate, // success type class = std::monostate, // failure type class = decltype(nullptr) // for detection idiom > class basic_result; -
ミュータビリティ ミュータビリティが型に埋め込まれいる(正直やりすぎ感ある) 内部で保持する値へのアクセスの制御に使われている 利用者はシノニムを利用する /// alias template
for immutable result template <class T = std::monostate, class E = std::monostate> using result = basic_result<mutability::immut, T, E>; /// alias template for mutable result template <class T = std::monostate, class E = std::monostate> using mut_result = basic_result<mutability::mut, T, E>; -
resultの構築 success, failureというfactoryクラスから作れる 関数からresultを返す場合も result<str, str> res(success<str>{"foo"}); result<str,
str> res(failure<str>{"foo"}); auto func(bool b) -> result<str, str> { if (b) { return success{"foo"}; } else { return failure{"bar"}; } } -
is_ok(), is_err() それぞれ正常値・異常値を保持しているかをboolで返す result<str, str> res(success<str>{"foo"}); res.is_ok(); // true
res.is_err(); // false result<str, str> res(failure<str>{"foo"}); res.is_ok(); // false res.is_err(); // true -
ok(), err() それぞれ正常値・異常値をboost::optionalで取得できる result<int, int> res(success{42}); res.ok(); // Some(42)
res.err(); // None result<int, int> res(failure{42}); res.ok(); // None res.err(); // Some(42) -
unwrap(), unwrap_err() それぞれ正常値・異常値を取り出す 値を持っていなければ例外を送出する result<int, int> res(success{42}); res.unwrap();
// 42 res.unwrap_err(); // raise the exception `mitama::runtime_panic` result<int, int> res(failure{42}); res.unwrap(); // raise the exception `mitama::runtime_panic` res.unwrap_err(); // 42 -
unwrap_or(T optb) 正常値を取り出す 値を持っていなければ指定された値optbを返す result<int, int> res(success{42}); res.unwrap_or(57);
// 42 result<int, int> res(failure{42}); res.unwrap_or(57); // 57 -
unwrap_or_else(F func) F: E -> T 正常値を取り出す 異常値errを持っていなければfunc(err)を返す
遅延評価したいときに使う 引数なしで呼べる場合はなしで呼ぶ result<int, str> res(success{42}); res.unwrap_or_else([](int err){ return err.length(); }); // 42 result<int, int> res(failure{42}); res.unwrap_or_else([]{ return 57; }); // 57 -
unwrap_or_default() 正常値を取り出す 値を持っていなければ指定された値を返す 正常型がデフォルトコンストラクション可能かアグリゲートである必要がある result<int, int> res(success{42});
res.unwrap_or_default(); // 42 result<int, int> res(failure{42}); res.unwrap_or_default(); // 0 -
expect(string_view msg) unwrap()の亜種で例外のメッセージに含める文字列を指定できりゅ result<int, int> res(failure{42}); auto ok =
res.expect("foo"); // panicked at 'foo: failure(42)' result<int, int> res(failure{42}); auto ok = res.unwrap(); // panicked at 'called `result::unwrap()` on a value: failure(42)' -
expect_err(string_view msg) unwrap_err()の亜種で例外のメッセージに含める文字列を指定できりゅ result<int, int> res(success{42}); auto err =
res.expect_err("foo"); // panicked at 'foo: success(42)' result<int, int> res(success{42}); auto err = res.unwrap_err(); // panicked at 'called `result::unwrap_err()` on a value: success(42)' -
map(op) op: T -> U 正常値okを持っているとき場合、op(ok) を適用しresult<U, E>として返す
異常値を持っていた場合はそれをresult<U, E>として返す result<int, int> res(success{42}); res.map([](auto ok){ return ok + 1; }); // success(43) result<int, int> res(failure{42}); res.map([](auto ok){ return ok + 1; }); // failure(42) -
map_err(op) op: E -> F 異常値errを持っているとき場合、op(err) を適用しresult<T, F>として返す
正常値を持っていた場合はそれをresult<T, F>として返す result<int, int> res(success{42}); res.map_err([](auto err){ return err - 1; }); // success(42) result<int, int> res(failure{42}); res.map_err([](auto err){ return err - 1; }); // failure(41) -
and_then(op) op: T -> result<U, E> 正常値を持っているとき、更に失敗する可能性のある関数へチェインする result<int, int>
res(success{41}); res.and_then([](auto ok) -> result<int, int> { if ( ok % 2 == 0 ) return success{ok+1}; else return failure{ok}; // ここを通る }); // failure(41) -
or_else(op) op: E -> result<T, F> 異常値を持っているとき、更に失敗する可能性のある関数へチェインする result<int, int>
res(failure{42}); res.or_else([](auto ok) -> result<int, int> { if ( ok % 2 == 0 ) return success{ok+1}; else return failure{ok}; }); // success(43) -
参照のResult mutabilityは保持する値のアクセスでも伝搬する str hoge = "foo"; mut_result<str&, str&> res(success<str&>{hoge});
res.unwrap() = "bar"; // now hoge == "bar" result <str&, str&> res(success<str&>{hoge}); // Error: immutable result’s unwrap returns const reference res.unwrap() = "bar"; -
as_ref() Resultの参照を参照のResultに変換する Resultが内部に持っている値のconstな参照を持つResultを作れる result<str, str> res(success{"bar"}); res.as_ref(); //
new result, containing a const reference into the original -
as_mut() Resultのミュータブルな参照をミュータブルな参照のResultに変換する Resultが内部に持っている値の参照を持つResultを作れる mut_result<str, str> res(success{"foo"}); res .as_mut()
// new result, containing a reference into the original .unwrap() = "bar"; // now res contains a successful value "bar" -
indirect() 要素型をデリファレンスした型の参照のResultを作る result<T*, E*> => result<T, E>
(T ok, E err) に対して { *ok, *err } という操作が要求されりゅ using vec_iter = typename std::vector<int>::iterator; std::vector<int> vec{1, 2, 3}; mut_result<vec_iter, vec_iter> res(success<vec_iter>{vec.begin()}); auto indirect = res.indirect(); // indirect: `mut_result<int&, int&>` auto& ref = indirect.unwrap(); ref = 42; -
indirect_ok() success型をデリファレンスした型の参照のResultを作る result<T*, E> => result<T, E>
(T ok) に対して { *ok } という操作が要求されりゅ using vec_iter = typename std::vector<int>::iterator; std::vector<int> vec{1, 2, 3}; mut_result<vec_iter, int> res(success<vec_iter>{vec.begin()}); auto indirect = res.indirect_ok(); // indirect: `mut_result<int&, int>` auto& ref = indirect.unwrap(); ref = 42; -
indirect_err() failure型をデリファレンスした型の参照のResultを作る result<T, E*> => result<T, E>
(E err) に対して { *err } という操作が要求されりゅ using vec_iter = typename std::vector<int>::iterator; std::vector<int> vec{1, 2, 3}; mut_result<int, vec_iter> res(failure<vec_iter>{vec.begin()}); auto indirect = res.indirect_err(); // indirect: `mut_result<int&, int>` auto& ref = indirect.unwrap(); ref = 42; -
ライフタイムについて 右辺値のresultで内部を参照して場合、寿命切れている可能性がる using vec = std::vector<int>; mut_result<vec, vec>(success{vec{1, 2,
3}}) .as_mut() .unwrap() // undefined behavior! -
dangling<T> 右辺値のresultの内部を参照した場合 dangling<std::reference_wrapper<T>>( dangle_ref )ができる using vec =
std::vector<int>; mut_result<vec, vec>(success{vec{1, 2, 3}}) .as_mut(); // returns `mut_result<dangle_ref<vec>, dangle_ref<vec>>` -
dangling::transmute() 参照が生きていることを確信する場合は明示的に中身を取り出す using vec = std::vector<int>; vec v{1, 2,
3}; mut_result<vec&, vec&>(success<vec&>{v}) .unwrap() // `mut_result<dangle_ref<vec>, dangle_ref<vec>>` .transmute(); // get `vec&`