Rust での unwrap, expect, panic, assert 周辺の話
手抜きって仕様でもバグでもない。強いて言えば仕様だけど、最終的な仕様ではなく、一時的な仕様で、将来的に取り除かれるものだ。あー、unimplementedって用語があるか。
2020-12-22 17:03:01Rust だとそもそも Option にせよ Result にせよ「検査をしないことが許されない」というシステムがあって、その上で「検査が手抜きであるか否か」のレイヤーで葛藤が発生するので、いずれにせよ検査すること自体は変わらないのよな。 .unwrap() は「手抜き検査」として優秀なラベルになっている
2020-12-22 17:03:05で、 .expect() を手抜き検査として使えるべきかというと、まあ微妙なんだけど私としてはせめて「お前(ユーザ)が悪い」か「俺(expect を書いた人)が悪い」くらいの情報は籠めてほしい、最低限それがあれば手抜きではないよと言い訳もできる、くらいのお気持ち。
2020-12-22 17:04:05無論 .expect() でそこそこちゃんとした理由説明があればかなり有り難いし、自分で書くときは必ずそのようにしているけど
2020-12-22 17:04:26@lo48576 はい.起こらない理由も(それが特別分かりにくい場合以外は)重要ではない.と考えています. 実装側のミスなのか,利用側のミスなのかは明確に分けて書くべきというのは賛成です.
2020-12-22 17:05:09で、そういう使い分けをすると「手抜きなら .unwrap()、ちゃんと考えたなら .expect()」という分類をもとにコードを読むときの注目を配分できるわけで、読むときにも嬉しいでしょうと
2020-12-22 17:05:42で、私が常々思っているのは「手抜きであるか否か」のラベルだけでなく、もう一次元加えて「お前が悪いか俺が悪いか」も名前とかで表明できてほしい、ということですね たとえば unreachable!() は「俺が悪い」を的確に表明できていてとても良い名前だと思うわけ
2020-12-22 17:06:39まあ理想的には match foo { Ok(v) => v, Err(e) => {/*ここで panic! なり unreachable! なりを使う*/} } なり .unwrap_or_else(|e| /* panic! や unreachable! */) を使いたいという立場では .expect() は手抜きなんだけど、じゃあ実際これを人々に求めるかというのは……
2020-12-22 17:08:14ちょっとコストが高すぎるというか、よーするに「これらの記法を .expect() 並に短かく表現できる、意味付けされたクラッシュが欲しい」というのが私の欲求です
2020-12-22 17:08:48Rust, unimplementedと別にtodoマクロがあって、あとでやるつもりがあるときはそっちを使うことができるっぽい。細かい
2020-12-22 17:09:23fn expect(&self, msg: &str) -> T の代わりに、 fn unwrap_which_must_always_succeed(&self, msg: &str) -> T と fn unwrap_or_panic(&self, msg: &str) -> T が欲しいと、私はそう言いたいわけです
2020-12-22 17:10:02twitter.com/lo48576/status… twitter.com/lo48576/status… この2つだな、私が今日言いたかったことは
2020-12-22 17:11:05foo.expect("hoge") が match foo { Ok(v) => v, Err(e) => panic!("hoge: {}", e) } である場合と match foo { Ok(v) => v, Err(e) => unreachable!("hoge: {}", e) } である場合があって、 .expect() はそれらのどちらにも使える雑なメソッドであるともいえる
2020-12-22 16:25:25ついでに .expect() だけでなく assert 系についても「俺が悪い」と「お前が悪い」の2種類あると嬉しいな、というよく似た話が twitter.com/lo48576/status… と twitter.com/lo48576/status… ですね。本質的には同じ欲求。
2020-12-22 17:12:35assert にも同様の濫用があって「クラッシュは仕様」と「クラッシュしたらバグ」が混在していて、その辺りは昔ブログに書いた: blog.cardina1.red/2019/12/19/don… > つまり、あらゆる assert は、プログラムが完全に想定 (仕様) 通りに動いたとするなら全く存在しなくても構わないように使うべきである。
2020-12-22 16:28:11/// Asserts that a boolean expression is `true` at runtime, as the specification. macro_rules! spec_assert { ($($toks:tt)*) => { assert!($($toks)*) }; } みたいなのは考えたことあるんですよ。 「仕様としてのクラッシュを assert 的に雑に書きたい」という。
2020-12-22 16:53:54twitter.com/lo48576/status… 「俺が悪い」と「お前が悪い」で意味付けされた2種類の .expect() が存在すれば、意味付けされていない .expect() を手抜きとして使うことができるので、全てが解決して世界に平和が訪れる、と…… (バグが消えるとは言ってない)
2020-12-22 17:13:57この辺りもアレで、そりゃ可能ならもちろん静的保証をしたいんだけど、それが無理で .unwrap() が必要になって、である以上は実装バグや仕様バグの可能性が否定しきれないので、「否定しきれないものの存在を否定するな」のお気持ちで起きえないブランチのエラーメッセージを考えているというところが
2020-12-22 17:15:25方向性としては、そういうのを module や crate として分離していって、ライブラリの API としては基本的に panic や unwrap なしに使えるようにするのが良いのだろうけど、境界を切って封じられる unwrap ばかりとも限らないからなぁ
2020-12-22 17:16:50Rust使いたい時点でわりとカッチリしていないと気がすまない用途である気がするし、カッチリするのが一番ではないか(カジュアルRustも探求できるかもしれないが……)
2020-12-22 17:17:02