Rust part19
レス数が1000を超えています。これ以上書き込みはできません。
89 それでも動く名無し 2023/01/10(火) 23:26:51.53 ID:pA5+SQtP0
痴漢ものAVと違ってこういうガチ痴漢は臨場感が違うわ
抵抗されて上手く行かなかったり、たまに他の客にバレて逃走してるからな
マジで興奮する
https://i.imgur.com/My4nuB1.jpg
https://i.imgur.com/ttXkWGH.jpg
https://i.imgur.com/upphmaU.jpg
https://gcol;le.net/product_info.php/products_id/763929/ref/15062/affiliate_banner_id/1
520 名無しさん@ピンキー sage 2023/01/03(火) 21:36:57.85 ID:AS4vmq4R0
不朽の名作が復活していたので
https://i.imgur.com/SGsDTp5.jpg
https://i.imgur.com/4R20ojB.jpg
https://gcol;le.net/product_info.php/products_id/863650/ref/15062/affiliate_banner_id/1 . :::';;;;: . . ..,,,;;:
. . :;;;;;:.:;;,, ..:.;;;;.:
:;;''' .:';;; . . .:.:;;;;;':. . . .,,,,;,,...,,
.:;;;' : .:;;;;; .: ,,,;;;,,, , .:;;;';;''' ''';;;;,,
. :.;;;;' . .: ;;;;;;;;'''' ';;;:.:.. ,;: . . ''''''"
';;:;' ''''; .:.';;;;,,;;.
''' ,.:.:';;;;,,,,
,、—-、 .;.';:;.:.: ;;;;;;:.;.;...
-、_ (_二ニ=っ,、;;;:.:;.:;...:.:...'''''''''''
`‐-、_ ( ´∀)f、 `''、:..:.:. .:
`-,ノ つ; /
(〇 〈-`'"
(_,ゝ ) `‐-、_
(__) `'‐-、,_..
`‐-、._ ResultがErrの時だけブロックを実行するifってどうやって書けばいいの?
if Err(_) = fs::read_to_string("foo.txt") { err() }
は通らないようだが >>12
エラーが起きた時は先に判断してreturnして
そうでなく正常値が得られた時はlet代入してインデントを下げずに書けないかな? >>15
それが少し前に入ってみんな喜んでた let else だね。
let Ok(value) = result else { return };
/* Ok のときやりたい処理 */
みたいにエラーは早期リターンして、正常系をインデント下げて書くみたいなことができる。
俺は Err は anyhow で呼び出し側に返しちゃう事が多いから正直あまり使わないけど。 Option/Result系は?演算子で素直に返すしそれ以外でもResultを返す関数に切り出して呼び出し側で?演算子にするからlet-elseは使い所がよく分からん >>15
そもそもreturnしない。err()の中身はエラーメッセージを出力後にパニックさせるので一方通行 expectじゃだめなのかな?
もう少しちゃんとしたロギングをしたいならlog crate使ってlog_expectみたいな形でunwrap 自分で書いといてあれだからやっばりexpect使うケースじゃないな
panicさせるよりもErrのまま最上位まで返してそこでログ出力含めてハンドリング expectってライブラリの動作確認とかサンプルコードとか、トップレベルで雑なことしたいとき以外使わなくない?
深めの階層で見かけたら身構えちゃいそう。 論理的に安全にunwrapできるってわかってる箇所には使うかな
もしパニックになるならそもそも致命的な論理バグがあるってことになるから
変にエラー処理するより早期に落とした方がいいってケース 横からですまんが安全なら unwrap() でよくない?
expect() にする理由ってなんだろう 個人的には Regex::new(r"").unwrap() みたいに自明ならunwrap
安全にunwrapできる理由を書きたいならコメントに書くよりexpectに書くかな なるほどね。自明な時に unwrap したいけど、仮に起きたときに原因調査しやすいよう expect というのは納得できる。 あ、違うか。unwrapできる理由を書くわけか。
paniced at 'ここでは 0 は渡ってこないので自信を持って除算する'
みたいな出力になるから割と恥ずかしいな。 >>26
これは俺も思ったw
まぁ自分しか使わないライブラリとかならいいかもね >>27
自分意外が使う前提のときのほうが except が必要だと思う。
外部から与えられる情報が仕様外の場合 (事前条件が満たされない場合) など
使い方が誤っているときに起こりえるなら except で説明があったほうがいいんじゃない?
内部的なロジック的に起こりえないと確信しているところは unwrap でいいと思う。
間違いがあったときに panic! してくれることには変わりないしバックトレースも出せるんで、
特に必要だと思う箇所だけ説明を付けておけば十分でしょ。 >>26
関係ないけどpanicに-ed、-ingつけるとkが差し込まれてpanicked、panickingになる
自分もRust触るまで知らなかった Optionを返す関数が2つ(以上)あり、
Someを得られるまで遅延評価で順に試す場合に、
func1(arg)
.or_else(|| func2(arg))
と非対称で手間なのを改善する方法ありますか?
例えば func1(arg) || func2(arg) と書けると便利だと思いました イテレータ化してchain()で繋げるとか?
でも手間は変わらないか >>30
ないんじゃね
何回も書く必要があるなら単に関数化する
||演算子で書きたければマクロ内独自文法作る >>20
今だいたいそうなっていてmainにエラー出力用のコードが散在している状態
エラーメッセージの出力先もコンソールのみじゃないのでexpectだと難しい気が >>33
いろんな問題をごちゃ混ぜにしすぎ
頭とコードを整理しよう 1.すべてmainまで返してmainでエラー処理をする
2.エラー処理用の関数を作ってそれを呼び出す
3.パニックハンドラでエラーメッセージを出力
くらいしか思いつかん。ググってもThe Bookに書いてある程度のことしか出てこなくて参考にならない
3はpanic!やexpectで脱出できるのは楽だけどハンドラへ渡せるデータが文字列のみでなのがいまいち
またエラー処理に必要な情報を文字列にエンコードする必要がある
2ならmainまで戻らずともエラー処理できるのでこの方向で実装中 log crateやtrace crateはチェック済み?
logは準標準、実装は好きなのを選ぶ必要がある
traceはtokio-rs製 >>32
|オペレータのstd::ops::BitOrトレイトのように
||オペレータのトレイトをRustコンパイラが用意してくれたらOptionも||で扱えそう >>37
info!やerror!ってpanic!やexpectと本質的に変わらないような。いずれにしろ呼び出し側で文字列の加工が必要
GUIへの対応方法もよくわからない。開発時はもちろんコンソールへの出力を使うけど
運用中のエラー出力はGUIのポップアップメッセージがメインになるし
あとソースコードは600行弱くらいだけどリリースビルドで生成されるバイナリは800KB弱もあるんで
これ以上でかくしたくないというのもある ロガーを設定/生成するコード
ロガーを使うコード
ロガー自体のコード
それぞれ分けて考える
ロガーを使うコードではファイル出力だろうが標準出力だろうがinfo!やerror!でコードを変える必要はない
使うロガーを変えたり設定を変えたりする
ロギングライブラリを使うのは自前で作るのは面倒だから 書籍で最初に読むとしたら
平家蟹の方?
それとも可愛い方? >>39
>GUIへの対応方法もよくわからない。
GUIのレイヤーまでResultを戻してErrならエラー表示をするだけ >>39
ログはログ。何が起きたか記録するだけ。ログレベルが何であれ副作用は起こさない。
エラーはエラー。発生したときにどうハンドリングするかはプログラムの性質で決める。
パニックはそのまま稼働したらまずい状況に陥ったら時だけ起こす。 アプリケーションのレイヤーでパニック起こすのはバグの時だけ ネットワーク前提にしてる時に、panicになるのはバグではないよ? うっわすっげー読みやすいコードと思ってよく見てみたら
過去に自分が書いたやつだった(´・ω・`) 過去のことを忘れていても過去の自分が考えることは今の自分が考えることとあまり差がない。
名前の付け方とかは何度考えても同じような状況なら同じ名前を付けるし。
書くときに想定する読み手が全くの他人のときと未来の自分のときではちょっと違う意識があるな。 一度も使ったことがない機能は書くことはできても読めると思うな、が基本
使ってから読め >>47
ネットワークこそ途中で途切れること前提に書かないといけない機能の最たるものだろ。エラー返してハンドリングしろ。 bool以外でも&&と||の遅延評価が使えるようになるわけか
欲しいね bool以外でもifを使えるといえばif letで
elseを省略することで3項ではなく2項演算のようになるのも&&と同じ
だがelseを省略できても{}を省略できなければ意味がない >>60
これはもっともな疑問
The Bookのどこかに書いてたように思うけど
ざっくり言えばprintlnはreferenceを取るから所有権は移動しない
DisplayトレイトやDebugトレイトのメソッドシグニチャを見ると分かる >>60
ついこないだ Teratail で同じような質問を見たぞ。
マクロは構文糖を作り出す仕組みなので展開形によっては所有権を取ることも借用なことも何にも使いすらしないということもある。 ある関数の&mut T型の引数として、T型の変数を貸すのは分かるけど
&mut T型の変数を又貸しするのが不思議
なぜmoveしたことにならないのか >>63
implicit reborrowのことかな?
&mut Tの又貸しと言ってるのがどういうケースなのかはコードかないハッキリはわからないな implicitly reborrowedされるとhogeが&mut *hogeになるのか
勉強になった
ありがとう 結局&mutを持っている間は専有しているから既存ルールに抵触することなく貸し出し自由自在という理解でいいのかな
&*でimmutableなreborrowも出来ちゃうもんね 暗黙で参照が外されることがあるからわかりにくいんだろうな
最初から暗黙の参照外しがなければよかったと思うが
後方互換性を大事にするなら、もう改善は不可能だな >>70
エラーを返すんだからpanicしないだろ implicit reborrowはThe Bookには書かれないし直感的でもないから動きが理解しにくいというのはよく分かる >>70
例えばtwitterアプリを地下鉄とか通信できない状況で起動して panic で落ちる事を考えてみろ。そりゃバグだろ。 Deref無しは流石にきついな
一気にRust書きたくなくなる気がする >>69
むしろderef含めたcoercionのおかげでRustは便利かつ読みやすいと思う
初心者の最初のうちだけは混乱するかもしれないけどそのデメリットを誤差にするほどの絶大なメリットがある 教えてくれてるのは本当にありがたいんですが、訳がわかんないぽ・・・・ マクロが展開するコードがあって、そこに&がついてるってことなんですか? せやで
Playgroundの左上のボタンでShow HIRするとマクロ展開等終わった後のコード出るから見てみ >>74
例がtwitterアプリって...通信が出来ない状態でも何らかのオフライン操作が行えるアプリであればそうでしょうが
仕様上、エラーハンドリングを行わなければならないとされていないならバグではないでしょ....
むしろ大したログやコンソールでの情報も出さず、「エラー:通信ができましぇん」なんて返されるほうが迷惑だわ >>82
バグかどうかは仕様次第というのはそりゃそうなんだけど、それじゃ建設的な議論にならんでしょ。
俺はError返しといたほうが利用側がハンドリングする余地があっていいと思うね。 >>82
Rust の文化にあまり詳しいわけじゃないけど panic を呼び出すのって C/C++ で言うところの assert 的なもんでしょ。
普通に考えたら panic が呼ばれるのはモジュールの仕様に対して使い方が間違っているかリカバリ不可能な場合。
逆に言えば使い方が正しくてリカバリ可能な状況で panic になるべきではない。
モジュールの使い方が完璧に正しいプログラムを書いても panic が引き起こされてしまうなら panic の使い方のほうが間違ってる。
絶対に通信が確立する状況で使えという仕様を設定すりゃあ間違ってるのは使い方のほうではあるけどさー、
ネットワークでそれは不可能な前提じゃん? ありえない前提を元にするのは不毛だろ。 >>82
> 仕様上、エラーハンドリングを行わなければならないとされていないならバグではないでしょ....
仕様バグ... ユーザーにとって不自然な動作をすれば開発者が仕様だと言い張ったところでそれはバグだよ エラーにも回復可能なエラーと不可能なエラーがあり、panicすると回復不可能な状態になるんだから、回復可能なエラーはpanicすべきじゃない。ましてや通常使用でしばしば発生する状態でpanicするのは言語道断だわな。 assertというかexitやな。推奨はされん、普通はデバッグで面倒な時くらいじゃないか。 >>88
exitは正常終了でも呼ばれるからassertのほうが意味的には近いと思うぞ マルチスレッド界隈ではスレッドの一つや二つ終了しても
回復不可能と思ってないでしょ The Bookにある回復不可能かどうかという判断基準は曖昧であまり役に立たない
Resultを伝播させてトップレベルでログ出力でもしてabortさせるのに比べて
問題発生箇所でpanicさせるメリットは処理をinfallibleな関数として定義出来ること
逆に言えばバグでも無いのにinfallibleな関数呼び出しでpanicで落ちるような設計はそれ自体がバグの可能性大 0か1かではなくバグが何個あるのかも重要
落とせば一つ?
進めれば二つ以上? >>86
開発者なんていないよ、みんなユーザーだよって言い張ったのがオープンソースだね ユーザーと開発者を分断して対立煽るのをやめようってことだよ 自分(たち)で用いるツール類だけは
自明な前提を満たしていない場合に限り
エラー処理をサボったpanicで終わらせてもよい
それ以外のpanicは状態矛盾など続行不可禁止で発生するが
正しくエラー終了させるべきものであり
panic発生はバグと呼んでも差し支えない >>98
panicをハンドリングしないのはバグかどうかは仕様次第と完全に認めてるのに、建設的な議論って・・・
作法的な話や、ユーザーフレンドリなUIでエラーメッセージを出したい、いきなり終了して欲しくないのであってもプロジェクトごとに異なるし、一般的な普遍性なんて「仕様上」に何が言いようがあるんだ?
オレはいつもこうしてます!という癖だったらいくらでも言えるし、100人集めてアプリケーションのレイヤーでpanicをハンドリングする/しないでアンケートしてどっちが人気かで正しさが決まるようなものではない。
最終的に「アプリケーションのレイヤーでパニック起こすのはバグの時だけ」とかいうのは明らかに間違ってるでしょ
そうするような特定のプロジェクトの仕様がたまたま(確率的に)一致するかもしれないが、それも一般化できる話ではないよ。
まあ、お望みの建設的な議論をするなら、アプリケーションをライブラリのように使用できる余地があるならResultsなどでErrorを返すのはとても良いですが、それでもpanicをハンドリングしてErrorで返すべきでは”無い”でしょうね
なぜならRustはそれを推奨していないし、Errorチェックしてをpanicに変換する方向性はあっても、panicをErrorに変える方向性は、仮にログ出力してもpanicの握り潰しやエラー情報の欠落に等しい。(なぜならログへのI/Oエラーになってる可能性もあるから)
それは、そもそもRustのpanicは言葉上は回復不能なエラーであり、バグではなくメモリーに物理的な障害が発生して配列インデックスが変になったとか、処理が続行できない、もしくはいったん特定の場所に戻って回復できないときに使われる思想。
なので、panic->Error変換処理が正常に働くかも怪しい。だからRustはそれを捉えず上位へ流して最低限やるスタックの巻き戻しのみ処理を推奨し、即座に終了させる(=プログラムが落ちる)
Linusはこのスタックの自動巻き戻しがとても気に入らないらしいが、理由は巻き戻し処理が正常に働く理由が無いからだ。
それを無理やり捉えて、スタックトレースが出るのが嫌、即座に終了するのが嫌、は分かるけどpanicで終了したからと言って仕様に書いてなければバグじゃないでしょ
これを癖でやってしまうのはtry-catch構文があるC#やJava系から来た人が多いのではないかな...? 誰もpanicをResultに変換する話はしとらんやろ て言うか>>99は前半と後半で矛盾してるしアホほど長文を証明してるw panicは仕様に書いてなければバグでしょ
どんなプログラム書いてんだよ エラーハンドリングという言葉をpanicをハンドリングしてResultにすることだと思ってたのか
そりゃ噛み合わないわな 例えばpythonのexitの代用としてpanicを使ったところで何の問題もない 問題ありまくり
同じexitがあるのにわざわざpanicで代用するメリットは何? まずは異常終了と正常終了を分断するメリットを知る必要がある >>105
へー今初めて知った
The BookのCommon Programming Conceptsあたりにそれっぽい記述はないし
中断したければpanicするしかないと思っていたよ
後学のためにどこで解説されているのか教えてほしいな >>107
The Bookの12章を復習して
https://doc.rust-lang.org/book/ch12-00-an-io-project.html
ただThe Bookは他言語から来た人が最初に読むチュートリアルとして用意されてるものなのでカバーされてない内容も多々あるし深い解説がされてるわけでもない点は理解しておいた方がいいよ 標準ライブラリのマニュアルでも panic! はバグを説明するために使うということは書いてあるね。
https://doc.rust-lang.org/std/macro.panic.html >>107
exitはプロセスの終了状態コードを伝えることを兼ねたOSシステムコールだから通常の言語には必ずある
そしてそのことを分かっていればRust初心者であってもstd::process::exitをすぐに見つけられる >>105
Pythonのsys.exitと同じ感覚でstd::process::exit使うほうがはるかに問題では?
少なくとも異常終了に使う分にはpanic!のほうがsys.exitに近いと思うよ
sys.exit()
https://docs.python.org/3/library/sys.html#sys.exit
・SystemExit例外を投げるだけ
・メインスレッドで実行して、かつ例外がトップレベルまで捕捉されなければ、通常の例外機構に従ってプロセスが終了する
→finallyとかwithでリソース解放書けばちゃんと解放される
std::process::exit()
https://doc.rust-lang.org/std/process/fn.exit.html
・無条件でプロセスを終了させる
・実行スレッドも他のスレッドも一切スタック巻き戻しが行われない
→デストラクタ呼ばれない _exit()はシステムコールだけどexit()はライブラリの関数
pythonのexit()やsys.exit()は基本的にexitcodeを設定してSystemExit例外を投げてるだけ
os._exit()がprocess::exit()に近い >>112
例外のある言語と同じ感覚でプログラミングするのが一番の問題
それ抜きにpythonと比べとも意味ないよ 一般的な他の言語におけるtry-throw-catchの機能が欲しいならば
それはRustやGoなどでは意図的に排除されていて存在しない
RustではResultで返すだけでよく利便性と効率を両立させている >>109
そこは見ているけど制御機構を説明しているところで同時に解説すべきなのでは
>The Bookは他言語から来た人が最初に読むチュートリアルとして用意されてるもの
なおさら他言語でメジャーな機能や実装と対比した解説が必要では
>>112
unwind不可なのは使いにくい場面が少なからずありそう
今作っているのはdrop使っているから強制abortは問題しかない RustのdropにはRcという具体的な目的がある
Rcが完璧主義ではないのでdropも完璧にする必要を感じない >>112
>Pythonのsys.exitと同じ感覚でstd::process::exit使うほうがはるかに問題では?
RustではResultをmainまで戻してからprocess::exitする
Pythonは例外という仕組みでランタイムがそれをやってくれる
panicはやってることがにてるからという理由で使うようなものじゃない >>115
そうではないよ?例えばrustの標準ライブラリのTcpStreamにはset_read_timeoutでタイムアウトを設定すると、それ設定値以上でpanicを起こす。
これは通信中に非同期制御やスレッド監視などをしないための苦肉の策でResultをmatchするだけという考えから外れて、回復不能としているのだがリードタイムアウトであろうと再試行するようなプログラムではpanicを考慮しなければならない。
一方でTcpStreamの多くはResult<>を返すので、高度なプロトコルを作るような場合、受け取ったデータなどを調べてpanicさせる方法などが公式ドキュメントにも述べられている。 The Bookに書いてるようにpanicを使う対象となるような回復不可能なエラーは常にバグによるもの
Rust groups errors into two major categories: recoverable and unrecoverable errors.
Unrecoverable errors are always symptoms of bugs. >TcpStreamにはset_read_timeoutでタイムアウトを設定すると、それ設定値以上でpanicを起こす。
readで設定したtimeout値を超えたらpanicすると言ってる?
少なくともリファレンスにはResultが返されるとあるんだが >>113
_exitはexit_groupのラップ関数だよ 言語の理想と実装は違うから食い違いが出ている。現実的には作者がめんどくさくなったり、ライブラリとそれを利用するレイヤーの区別があいまいな場合などに大域脱出とスタック巻き戻しがしたいだけで回復可能な場合にもpanicを投げてしまう実装もありうるのでバグではない。標準ライブラリでさえそうなのだから > 受け取ったデータなどを調べてpanicさせる方法などが公式ドキュメントにも述べられている。
これはpanicを使えということじゃなくサンプルコードとしての簡潔さ優先してるだけ
改善はされてきてるけどunwrapがあちこちにあるのと同じ
箸の上げ下げレベルで手取り足取り教えてもらうことを期待し過ぎじゃないか >>118
うん別に良いデザインではないよ、そこは同意する
俺は「同じexitがあるのに」という表現が招きかねない誤解に釘を差しただけです >>124
もちろんドキュメントで述べられてる通り、Resultが一番の選択肢で回復可能で返せるのにpanicを使うのは愚策でしょう。だから理想はとそういってますよね?
手取り足取り教えてもらうのはどちらなのかというよりも、どうしてもpanicで終了はバグだという理想・意見をとゴリ押ししたいだけに見えます。
これは(=回復可能なのに)panicを使えということじゃなくというのは強く同意ですが、そうなっていないものを使用している現実があるという話でpanicを書いてないことに期待し過ぎじゃないか?ということになります >>119
Rustでそのような形のpanicの使われ方はしませんしpanicは起きません
タイムアウトもio::Resultでio::Errorが返るだけです
これはC言語で書いてSO_RCVTIMEOでタイムアウト値を設定してreadで-1とerrnoが返る時と全く同じ状況です
言語独自の例外機構を持つ言語は複雑にしてややこしくしているだけなのでその考えをまず捨てるところからスタートしましょう >>126
今回のケースでも標準ライブラリはpanicを起こしていませんしpanicを起こす仕様はありません
もしpanicが起きたのならばそれはあなたがResultの処理をせずに強引にunwrap()したためであり
あなたのコードがpanicを引き起こしたのです The BookにmainまでResultで戻す実践的な設計方法って解説されてる?
機能の説明はあっても実装するうえでどのようにしたらいいのかってところは抜けているような
ググるとstd::num::*を返す例、Stringを返す例、enumを返す例などが出てくるが
どのように使い分ければいいのかって点は不明
このスレ見ていてもこの部分に関する資料を提示している人は見かけないし
>>124
>箸の上げ下げレベルで手取り足取り教えてもらうことを期待し過ぎじゃないか
箸文化圏なら要らないだろうがスプーン・フォーク文化圏なら要るんじゃね
他所と大きく違うのであれば十分な説明を求められるのは当然では > 例には、不正なデータを渡されたパーサとか、 訪問制限に引っかかったことを示唆するステータスを返す
> HTTPリクエストなどが挙げられます。 このような場合には、呼び出し側が問題の処理方法を決定できる
> ようにResultを返してこの悪い状態を委譲して、 失敗が予想される可能性であることを示唆するべきです。
> panic!を呼び出すことは、 これらのケースでは最善策ではないでしょう。 >>129
mainまでResultで戻すにはResult型を返す関数を書くだけ
何も難しいことはなく複雑なこともなくシンプル
Resultは単なる型の一つでOk値とErr値のEnumに過ぎない
Rust言語はResultに対し例外機構のような特別扱いをしていない
ResultはTryをimplしてるから『?』オペレータを適用できるなどくらいしか他の型との違いはない
したがって新たに勉強すべき点はそこだけだが『?』は使わずとも同じことを記述することができる >>131
Result<T, E>のEってなしに出来たっけ?自分が言いたいのはそういう話なんだけど >>132
特別扱いはないので自由
例えばbool相当としてResult<(),()>型を使ってももちろん機能する
またOption<T>相当としてResult<T,()>型
通常はそれぞれ前者を使って必要なところで初めてResultへ変換が普通 >>129
> The BookにmainまでResultで戻す実践的な設計方法って解説されてる?
12章はどうだろうか、minigrep を作るところ >>129
12章に書いてるでしょ
それに一連のレスで書いてる設計指針はRust特有の話じゃないよ
アプリのトップレベルに集約エラーハンドラを用意するのは例外機構のある他の言語でも同じだし
エラー発生時にexitcodeを設定してプロセス終了させるのはUIに相当するレイヤーの仕事だからそこまで戻してハンドリングするのも他の言語でも同じ
pythonだとしてもいろんなレイヤーでsys.exitするのは典型的なダメなやり方 RustはResultを特別視しない
例えばGoのようにRustで(正常値, エラー値)とタプルで返す関数群でプログラミングしても同様に動く
じゃあなぜResultを用いるのか?
・統一した型があった方がいい
・二値ではなく二種のEnumでよい
・便利なメソッド群を標準で用意できる
・標準ライブラリをResultで統一できる Rust特有のエラーハンドリングの実践的な知識はanyhow/thiserrorの使い方を学ぶといい
それらのcrateを使わず自前でやるとしても何を用意する必要があるのかわかる 副作用がない関数なら大域脱出を使うべきでないのは分かる
副作用に寛容で大域脱出に不寛容なのは分からん >>139
それは初心者には混乱の元
panicとか言ってる初心者たちが最初にすべきことは一つ
「unwrap()を使わずにプログラムを書く」 >>109
>The process::exit function will stop the program immediately and return the number that was passed as the exit status code.
>This is similar to the panic!-based handling we used in Listing 12-8, but we no longer get all the extra output.
>・・・
>Great! This output is much friendlier for our users
exitの使用目的はpanicによる不必要なメッセージの抑制に読めるけど?>>112で触れられている副作用なんか完全スルー
それに明らかに大域脱出を意図した使い方
裏を返せばpanicのメッセージ出力が問題にならないのであればpanicでも構わないとも取れる Rustをきちんと学ぶ気があるならば
まずはpanicもunwrapも使わずにプログラムを自在に書けるようになること
そういう基礎ができていないから勘違いしてpanicにこだわろうとしてしまう 評判の悪いthe book 日本語版にすら使い分けの記述あるのに、それを無視して回復不能なエラー処理以外でpanicを推奨しようとするのは何なのかね。
エラー処理
Rustには例外が存在しません。代わりに、回復可能なエラーにはResult<T, E>値があり、 プログラムが回復不能なエラーに遭遇した時には、実行を中止するpanic!マクロがあります。 the bookを一通りきちんと読んだならunwrapは極力使うべきではないものだと理解できるはずなんだけどな >>138
panicで大域脱出して構わない状況ならmainで必要な情報はreturnするか否かだけ
これはResultが保持しているのでEは不要 >>147
すまんけど全然わからない
TとEの両方がないとResultが存在できないと思うんだが >>149
Eをunit typeにすることをEをなしにすると言ってることは理解したが
>panicで大域脱出して構わない状況ならmainで必要な情報はreturnするか否かだけ
これは全然わからない
大域脱出したいからpanic使いたがってるという動機部分は理解した exitcodeをちゃんと実装したい時は
process::exitのリファレンスに書いてるように
mainの戻り値にTerminationを実装した型を指定してprocess::exitは使わずmainから戻り値を返す方法が今は推奨 >>151
処理継続不能なら終了するしかないからね。panicで終了しようが、mainまで戻ってからreturnしようが大差ない >>153
いやいや、深いところから脱出するのにResultだと途中の階層すべてで返さないとダメだからコーディングの手間が全然違うだろ >>154
でもアンチpanic君?はその手間を正当化したいっぽいじゃん 日曜プログラマーの作るソフトときちんと作るソフトで基準がズレてる感じ >>154
実際にコーティングしてみれば手間はほぼないと分かる
・返り値型をResult<>で括る
・ライブラリも自作もResult返す関数を上位へスルーするなら「?」を後置
たったこれだけの話
もちろんその場で処理したいResultや情報を増やしたいResultは今回の話以前に元々処理しているはず
回復不能なエラーなんて滅多に起きず
ほとんどはその場か上位関数で何らかの対応処理するエラーなのだから
panicは滅多に必要としない mainまで戻るってことはmain肥大化と同義。積極的にmainを複雑化させるべきいう主張か
もちろんプログラミングの定石的には1関数にあれもこれも詰め込むのは悪手だよな
panicダメ言っている人は実用的なプログラムを書いたことがあるのだろうか >>158
> たったこれだけの話
それを途中の階層全てでやらんとダメだろ
お前こそでかいプログラム組んだことないんじゃね?
そもそも途中の階層は自分が組んでるとは限らんし デストラクタ内でexitを使ってはいけない
デストラクタ内で利用可能な関数の中でexitを使ってはいけない
すべての関数はexitを使ってはいけない
こういうことかな そもそもPythonのsys.exitだって本当は100点満点の正解コードを目指すなら独自例外作れって話だし
その程度の雑さでいいならRustでも雑にpanicしたっていいじゃない
教科書的な話じゃなく、もっと実利的な問題が何かあるなら教えてくれよ >>159
mainまで戻る必要はない
通常は必要なところでエラー処理をする
>>160
エラー処理するところまでの途中のみでResult通過
だから手間なんてない 深い再入階層からジャンプして戻って来れる便利な例外 >>165
デストラクタ内で例外を投げないのは成功 >>164
> 通常は必要なところでエラー処理をする
それは回復可能なエラーの話だろ
> エラー処理するところまでの途中のみでResult通過
> だから手間なんてない >>168
まともなプログラムならば回復可能か否かに関係なく各々のエラー処理をきちんと行う
そのエラー処理を最下位関数内の関数呼び出し毎に行うのではなく中位の関数である程度まとめて行う時にResult返しとそのスルーで集める
このような下位のエラー処理をまとめて行うことはRustに限らずどのプログラミング言語でも同じ >>164
途中で送信してしまった
> エラー処理するところまでの途中のみでResult通過
途中?
main まで帰る話はもういいのか?w >>169
回復不可能なのにどんなエラー処理するつもりなんだよw >>170
mainまで戻したいかどうかはそのプログラムの自由であってRustとは何ら関係がない
これは例外のある言語でも同じ
例外catchする場所はそのプログラムの自由 元々の回復不能な話はこれだけど
>>47
>> ネットワーク前提にしてる時に、panicになるのはバグではないよ?
おもちゃなプログラムを除くとそういう状態は回復不能と扱わなくて
回復不能はまず滅多に起きることではないよね
そのケースだとネットワークが使えないことを知らせて使えるようになるまでデータを維持して待つ
panicで終わらせてよいのはかなり限定された場合だけとなるよね 回復不能連呼してる人が具体的にどんな状況でのエラーを想定してるのか説明しないのに建設的な議論になるわけがないな プログラムを使う側からしたら回復可能かどうかなんて些細な問題で処理できる or できないがすべて
そもそも技術的に回復可能であってもそのような実装になっていないプログラムなんて腐るほどある >>173
既に書いたように回復可能か否かに関係なくエラー処理は行われる
そして上位でまとまった共通なエラー処理をする時に
途中関数での?オペレータ付加とResult返しのみでRustでは実現できる
例外機構という複雑で非効率な仕組みは必要ないという話が本題 >>174
>そのケースだとネットワークが使えないことを知らせて使えるようになるまでデータを維持して待つ
それは対話式のUIを持っているアプリ限定。CLIのアプリならエラーメッセージを出力して終了するのが一般的 >>178
その場合でもライブラリの奥底からResult返しの連鎖があり
エラーメッセージを出力する上位まで来てからそのResult処理をする形に変わりなし
個別プログラムの方針とは関係ない共通の話がなされている コマンドラインオプションでリトライ指定機能追加するときに
なんでもかんでもpanicだと泣きを見るぞ fs::read_to_stringでエラーが返されたら回復不能なんでしょw >>181
接続失敗するだけでpanicにしてるらしいからな >>178
curlとかリトライできたりするCLI使ったことないの?
元の質問者が>>39でGUIへの対応方法がわからないと言ってるのも本質的なエラー処理を理解せずpanicしてるからだぞ >$ cat foo.txt
>cat: foo.txt: そのようなファイルやディレクトリはありません
ここで正しいファイル名を入力しろなどと言いだすCLIアプリは相当レア >>186
その状況でエラー出して終了させるのは良いとして、まさかpanicさせるなんて言わないよね? >>186
$ cat foo.txt bar.txt
とでもやってみろよ 「回復不能」って言葉がよくないんだろうな
エラーの種別についてある程度の前提知識を持ってないとなんでも恣意的に回復不能に分類しちゃう 通りすがりのF#erですが、RustでもRailway指向ってオススメですか? F#の記事ですが、ResultかpanicするべきかをRailway指向を考えた方が記事にしてます。
https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/
ドメインエラーはResult、それ以外はpanicの方が良いって言ってるぽいかな。 >>183
その場合でも例えばこのように
Result処理せずにunwrap()してpanicさせたら困ることをpanic派が気付いているかどうかだよね
fn foo(path: impl AsRef<Path>) -> String {
let text = fs::read_to_string(path).unwrap();
// テキスト処理略
} >>191
それは例外を使うべきケースと戻り値でエラーの種類を表現すべきケースの区別を例外機構のある言語を前提に語ってるもの
エラーの種別についての前提知識としては役立つがRustのResultとpanicの区別とはまた違う
その人が3種類に分類したエラーのうちPanicsだけがRustで言うところのpanicを使うケースにあたる >>191
その文書のResultやpanicはもちろんRustのそれらとは意味が異なるよ
そして代わりに例外でエラーを返すと書かれていてもRustには例外は無いわけでw 複おじ死んだんじゃなかったの?
いい加減コテつけてくれよな
あと新年の挨拶もまだだろ?まだ1月のうちにやっとけ >>194
>>193
Rustは例外が無い>try-catchやthrowが無い。
という理解で有ってます?
となると、Resultの守備範囲が広くなりそうですね。 panic・回復不能エラーは滅多にない。
Ruby on Rails でも滅多にない
ファイルが存在しない、数値に変換できないなど、予測可能なエラーは例外。
こういうのは普通に起きるエラー
たぶん、panicを使う香具師は、書き捨てのコードだろ。
リソースの解放・後始末が面倒くさいから >>191
これに尽きるでしょ
・Panics.
These are errors that leave the system in an unknown state, such as unhandleable system errors (e.g. “out of memory”) or errors caused by programmer oversight (e.g. “divide by zero,” “null reference”).
要はpanicはプログラマーにはどうしようもないメモリー不足とかプログラマーの想定外の状態になったときに使うものだよ
他のプログラム言語でもAssertとか使うだろ >>196
そう
Rustはtry throw catchの例外機構を意図的に排除している
例外機構の導入は複雑になるだけだなく効率面での不利や他の機能との相性など様々な問題がある
そのためRustやGoなどのプログラミング言語では例外機構が導入されていない
例外機構のある言語の多くでは例外が素通りする途中の関数でその宣言をしないためコードを見てもその有無が分からない問題もあった
Rustでは戻り型のResultによりそれを扱うことの有無が明確になっている
さらに"?"オペレーターによりResult値が下から上へ素通りすることも明確になっている
つまり簡素化と効率化と可読性の向上全ての両立をRustは実現している >>199
> 例外機構のある言語の多くでは例外が素通りする途中の関数でその宣言をしないためコードを見てもその有無が分からない問題もあった
そりゃ途中の関数にはその例外は関係ないからね
関係ないものを見る必要はない >>200
コードレビューやリファクタリングや移植作業をしたことがあればそれがコードに見えないことは非常に困ると分かる
Rustはそれがはっきりと分かるため可読性が良い mainからリターンするのであればプログラムの終了に至る条件が増えるほどmainの条件分岐がでかくなる
ってことも理解できない人がpanicダメとか言っているんだね このスレ見ていると、Rust勉強しているのにコールスタックとかスタックフレームを理解していないやつて意外と多いのが分かるな。
そもそもmainはスタックの根元で、OSとかプログラマーから見てプログラムそのものの代表だろ。
正常なフローならmainで始まってmainで終わるのは定石であって、正常なフローを破壊するpanicは異常事態。
Rustはスタックを重視してResultとか?演算子とか整備しているんだから、わざわざ用途外でpanicを使うのはRustのコンセプトを理解できていない証拠。そんなにスタックフレームのフロー制御が嫌ならRust以外を使ったら? >>202
そもそもpanicするのはメモリ不足くらいしかない
それも最近はtry_reserve()などpanicを避ける対応も増えてきたがまだpush_within_capacity()など安定化していないな
あと固定サイズ内でやりくりするArrayVecのtry_push()を使うこともよくある 例外がないせいで、複数ライブラリ使うとエラー型を合成する必要があってつらいわ >>200
入れ子になった関数の途中かどうかなんて関係なく、自身が呼び出した関数から出る例外に無関係な状況など考えにくいんだが >>206
自分が関与しない例外なんだろ?
その関数にできることなんてないはずだが? >>205
それは"?"オペレータのところでFrom::from(err)変換が自動適用されるので変換をimplしておけばよいだけ panic派の主張は「肥大化するとなんかダサいからエラー処理サボってpanicさせとくわ。コード量は減ってスッキリするから格好いいでしょ」という風にしか聞こえない
どれだけ肥大化しようとも想定が必要なエラー処理を省略しては駄目だろう >>207
例外がそこを通過するのか否かそこを見ただけで分からないからレビューでもリファクタリングでも何でも不便すぎる
一方でRustはそこがはっきりしていて可読性が良い
未処理な事項があって上位へ処理を委託していると明確に分かる >>205
とりあえず合成したいならanyhow使えばいい
勝手に全部合成されるから使い勝手は例外と変わらんと思うよ >>212
anyhowで勝手に合成されたエラー型への参照をmain()のifで場合分けしようとしたけど
JSのinstanceof 演算子的な処理のやり方が分からんかったわ >>211
だから不便すぎるとかふわふわした言葉で語るなよw
具体的に何が不便なんだよ >>210
panicはバグとかメモリー不足とかでどうしようない状態になったときに呼び出されるもので通常のエラー処理なんてできないってことぐらい理解しなよ >>213
そういう使い方をしたいならば例えばライブラリA,B,Cに対してMyErrorをEnumでErrorA,ErrorB,ErrorC各収容した方が扱いやすさも効率も良い
そのEnumに格納するだけのコードimpl From<MyError> for ErrorAしておくだけで簡単 >>214
Rustでは通過する途中の関数においても"?"オペレータの有無で未処理エラーを上位へ委託しているか否かが明白に分かる
だからコードレビューがしやすいし機能追加やリファクタリングする時にも見落としがない
一方で例外機構のある多くの言語では通過する途中の関数を見ても例外が通過するのか否かすら分からない
範囲を大きく広げて探し回らないといけなくなりムダな作業も発生して不便である >>205
自分のcrate用にエラーenumを定義して下位のエラーはそれにwrapするんだよ
その辺を楽させてくれるのがthiserror
カスタムな例外を定義しなくてもBulit-inの例外を使い回すだけで規模の小さいプログラムなら書ける例外+継承のある言語とは違うところ
Rustの場合はioエラーのみとかじゃなければ常にエラーenumを書くようになる >>218
だから自分が関与しない例外を調べて何をするんだ?
例外使うなら例外安全に作るのは当たり前でその関数が関与したリソースはその関数がきちんと始末することでなんの問題もないでしょ? >>217
そこは自由度があるから自分で必要性に応じて仕様を決めるべき
完全にサボりたいならanyhow等を使えばよいがその代わりにdynによる非効率さと分類の扱いにくさがトレードオフ >>220
中間関数は関与していないのではなくcatchしないことで例外エラーを上位へ移譲している
機能追加やリファクタリングではその処理位置が変わり得るから無関係ではなく把握する必要がある
同じことをRustならば上位への移譲があるか否かが明確に分かるため非常にやりやすい >>222
> 機能追加やリファクタリングではその処理位置が変わり得るから無関係ではなく把握する必要がある
そりゃその関数が関与するような修正するならな、当たり前
でも全ての関数が毎回関与するわけじゃないだろ
なんかダメな理由を必死に探してるとしか思えないけど >>223
コードレビューでも同じ
上位への移譲の有無がはっきり分かるRustは可読性が良い メモリ不足でパニックとか言っている時点で全く判かっていないの草
メモリのアロケーションに失敗した場合パニックしてもその動作は保証されない
なぜならスタックの巻き戻し中にメモリのアロケーションが試みられないことを保証するのは難しいからだ
そのアロケーションに失敗した場合に二重パニックになる
パニック時にアボートする場合は別だが、その場合はリソースやロックの開放もれが発生する可能性があるね >>225
全然ここの話を聞いてなかったけど、C++だとデストラクタは内部で例外を発生しては
ならないと決まっているから、デストラクタ内部ではメモリー不足例外も
発生させてはならない。 >>226
[補足]
Java などでは、例外を投げるには throw new SomeException(); のようにするが、
C++ では、throw SomeException() のようにする。つまり、前者はヒープから、
後者はスタックから例外オブジェクトを確保する。なので、例外throwそれ自体は
メモリー不足にはならない。 Resultでエラーを表現する一番の目的は
抜け漏れなくエラー対応してることを
シグニチャーを見ただけでわかるようにして
簡単に静的チェックができるようにするため 今日もpanic警察が必死です。MISRA-C警察みたい >>225
メモリー不足で動作が保証されないなんて常色だろ
同じくプログラマーの想定外の場合でも動作は保証できないからそういう時は余計なことしないで早期に終了させるしかない
panicってそのためのものだよ
なので個人的にはrustのスタック巻き戻しはやり過ぎだと思ってる >>230
必要ないと思うなら使わなきゃいいのになぜか鬼の敵みたいになってるの草 panic使う人は使っても困らないような
プログラム開発してるだけ
本人が困ってないならそっとしておけ panicを使う理由が対応コード書くのがめんどくさいとか見通しとか回復可能でも呼ぶぜってところからきた話なので
回復不能エラーで呼び出された場合とか当たり前なところ話しても議論にもならんやろ > 本人が困ってないならそっとしておけ
と言いながら構わずに居られない>>233であったw >>233
元からそういう話だぞ。panic原理主義者が発狂しているだけで 簡単にpanicされると困るようなプログラム作りたいからこそrust選んだのでは JavaやC#あたりの経験もなくPythonやJavaScriptしか触ったことがない層がRustをやろうとしてるんだろうな
そうでもないと説明がつかない 言語というよりはエラー種別やドメインエラーをきちんと定義するようなアプリやシステムの開発経験がないからだと思われる 自分以外がすべて同一人物が書いたかのように見えるなら病院に行きなさいって GoとかRustとか例外サポートしなくなった言語、標準でスタックトレースサポートしなくなった辛さにみんなどう対応してるのかね。
エラーメッセージで grep とか、ログからコールスタックを想像とか、かなり辛いんですけど。 スタックトレースサポートされてるぞ
だかスタックトレースないとどこでエラーが発生したかわからないような作りは見直すべき c++勉強しているけどやっぱりrustっていいんだな
c++の場合どのオーバーロードが足りないかもエラーをチラ見しながら勘で判断するしかない
少なくとも能力の低いワイはそうやって対処している 例外連呼しているくせに具体的なことを書かない時点でエアプか煽りなんだろうな
例外機構を持つ処理系はエラー処理を局所化できるメリットがある
同様のメリットをRustで得るにはどのような実装が推奨されるんかな? そこまで言うなら見せてもらおうか。
スレッド間エラー転送とやらを。 >>229
Rustの利点に対して「いらない情報」と言い張るのはまるでアンチのように悪意があるなあ
可読性の向上という開発効率でのメリットと実行効率のメリットさらにスレッドやコルーチン実装のタスクからも伝播させることができるRust方式は非常に優れているよ >>254
だからその情報がなぜ必要なのかを書きなよ
毎回可読性の向上って念仏唱えられても困る >>247
Rustは標準でスタックトレースをサポートとしている
何でもいいからコンパイルして実行したことあればRUST_BACKTRACE=1と環境変数をセットすれば出力すると表示されることすら知らないのは不思議
ちなみにstd::backtrace::Backtraceによって自由自在に好きなところでバックトレースをキャプチャすることも出来る >>251
Rustでもエラー処理を上位に委託してエラー処理を一箇所に局所化できる点は全く同じ
いくら初心者でもネット上のBOOKやサンプルや書籍など見てコードを書けばすぐに分かること >>255
254じゃないけど、メリットは分かりやすいけどなぁ。
関数のインターフェイスに、関数の作用に関する情報がまとまっていたらそりゃ便利だろう。
例外を投げる関数の場合、例外に関する情報は関数のマニュアルを参照するかソースを参照するしかないことがほとんどじゃない?インターフェイスを見ただけで例外を把握できる言語てあったっけ? ML系の系譜の言語はまあ大体そうなんじゃね
そしてRustでもdyn Errorで返されたら結局同じことやらされる羽目に…… >>258
だから例外に関与しないのになぜそんな情報いるんだ? >>260
マジでそれ言ってるの? マジで?
例外を無視しても「例外が投げられる」という作用は消えないよ?
例外が投げられても「俺関与しないから」と言って無視するの? 例外に関与しないってどういう意味なんだろう?
ガン無視するって言ってるんだろうか? >>261-262
いちいち曲解するなよ
下位で発生した例外は何もしなければそのまま上位に伝搬するだろ
例外安全に作られてればその関数で確保したリソースはちゃんと解放される
それは下位の例外の種類に依存しない >>263
「上位に伝達する」という意味わかっている?
>258がインターフェイスの話をしているのは理解できている?
関数の呼び出し元からすれば、呼び出し先で投げられているのか、さらにその先で投げられているのか、とか関係なく「関数を使ったら例外を投げられた」だよ。関数のユーザーからすれば「投げる例外くらい明確化するのが常識だろ」と思うよな。
関数が例外を投げるのに、その関数の作者が「例外に関与しないからオレ知らね」とか言って逃げたらブチ切れるわ。そんなんだったら例外全部catchして関数の外に漏らすな、と。 >>263
例外安全って意味広すぎて
強い保証したいときとかあるやろ Javaの検査/非検査例外以降の約20年余りの試行錯誤の結果辿り着いた現時点でのベストプラクティスを採用したのがRustやSwiftのエラー処理モデル
C → Java → C# → Go →Swift/Rust >>263
そうやって
ヌルポで落ちるプログラムを量産したんだよね まぁ、Result使うとしてもtry catch finallyブロックみたいなフローが欲しいというのはわからんでもない。
関数から抜ける時にResultを漏らさず処理したいというのはたまにあるし。 >>263
catchとかしないの?回復処理したり付加情報付きの例外投げ直したり
そのためにはcatchすべき例外が上がってくるかどうか知らないといけないんだけど この件でプログラミング言語が最低限サポートして欲しい点2つ
ある関数(ライブラリ)を使おうとした時に
①その関数やその子孫からエラーや例外が上がってくるのか、それとも全て処理済なのか?
これが奥深く辿らなくても局所的に把握できること
ドキュメントベースはそのミスや見落としが生じるため不可
②エラーや例外が下から上がってくる関数を用いた時に、
その処理をせずに続行してしまうコードを書いてしまったら、実行前に言語システムにより防げること
例えばRustならば①は返値がResult型かどうかですぐに把握できる
②は型システムによりRustコンパイラが型不一致や未処理放置Resultの存在を伝えてくれる
Rustは現在ベストなプログラミング言語と言い切っても過言ではない >>270
高い信頼性が要求されないプログラムなら
例外と集約エラーハンドラさえあればいいんだから
Rust的なモデルが最適化どうかは要件次第だよ
結局はトレードオフ >>268
今でもResultを漏らさず処理できると思うんだけど
できないのってどういう状況? >>271
趣味のおもちゃプログラムでなければ信頼性は必須だよね
それだけでなく>>270の最低限の2点はプログラマーにとっても必要なこと
①を満たせない言語では無駄に調べまくらなければいけない
②を満たせない言語では無駄に実行時デバッグを強いられる
トレードオフと言われても信頼性と開発効率を両方落とすのは割に合わない 使いたい関数だけじゃなくて、その関数が使ってる関数、更にその関数が、、、って調べていかないといけないのが無駄にコスト高になるんだよね。 >>274
使いたい関数のシグニチャ見れば分かることだろ????? >>275
Rustならば使う関数のシグネチャを見ればResultが返ることで>>270の①を知ることができるけど
例外機構の言語の多くはシグネチャには情報がないため完全に信頼できるドキュメントでもない限り下流の関数全調査になるかな
②の処理忘れ防止機能も含めてRustが最も整備されている言語という感じ >>264
だから上がってくる例外を処理する必要があるならその時に調べればいいでしょ?
例えばprintfみたいな関数作ってる時に下位のputsみたいな関数がI/Oエラーで例外上げてくるだろうけどそれはそのまま上位にあげるだけだろ
いちいち意識する必要はない
>>265
強い保証の意味がよくわからんが自前でキャッチして処理すれば良くね?
>>269
全ての関数で回復処理が必要なわけじゃないし情報を付加するだけなら例外の種類を問わずにキャッチしてスローすればいいでしょ
すべての階層で事細かく例外をキャッチしてスローし直すなんてことは普通やらないよ >>270
言いたいことはわかるけどそれを実現する手間が掛かりすぎると思う
そもそも例外を上げるかどうかだけを見たいならnoexceptを真面目に実装すればいいだけだし >>280
それを実現する手間が掛かりすぎる、という視点がむしろ既に間違えているのかもね
従来の例外の枠組みを持たずにRustは>>270の二点をシンプルで効率よく実現してしまった
つまり従来の例外の枠組みよりも利便性と信頼性の高い新たな枠組みが実用的だと示されたのだから
従来の例外の枠組みを捨てるべき時が訪れたと解釈する方が正しいのかもしれない >>272
あ、漏らさず処理はできるな。ごめん。
言いたいのは「似たようなエラーをまとめておいて、修正も一括で行う」のイメージだった。
例えば「関数内で細切れでファイルに書き込んでいるとき、どこかで書き込みエラーが出たらロールバックしてログを取って再書き込みする」とか。
>>279
やっぱり全然理解できていないな。
その「printfみたいな関数」を使う「上位」のプログラマーはどうすんだよ。「例外に関与しないからオレ知らね」か?
そんなんだったら例外全部catchして関数の外に漏らすな。 >>281
> 従来の例外の枠組みを持たずにRustは>>270の二点をシンプルで効率よく実現してしまった
シンプルだけど生産効率は良くないよね?
って言ってるんだけど... >>282
上位で処理する必要があるならその時に調べればいいだろ
途中の関数で逐一調べる必要はない
そもそも最下位の関数に新しいエラーが定義されたらrust使いは全部調べ直すのか? >>279
その調べるかどうかをどう判断するかって話なんだが…
putsがI/Oエラーを上げてくるって知ってるから無視して上に上げるって判断ができるわけ
じゃあライブラリXの関数Yは無視すべきなのかそうでないのか?ってこと >>284
どうやって調べるのか、具体的に考えた?
インターフェイスしか提供されていなくて、ソースコードの無いライブラリとかでどうやって調べるの?
エスパーか神様でもなければ不可能だね。 >>283
むしろ>>270の二点をサポートしているRustは開発効率が高いでしょう
それらをサポート出来ていない従来のプログラミング言語は開発効率も信頼性も低いわけです
開発効率と実行効率と信頼性の高さを両立させたRustの地位は揺るぎないと思われます ダックタイプ系の開発効率を求めるならRustは選択すべきじゃないよね。メモリの取り扱い見ればRustは「作法を強制する言語」だということは明らか。
そういうのはPythonとかRubyとかスクリプト言語があるんだからそっちを選ぶべき。 >> 279
> 強い保証の意味がよくわからんが自前でキャッチして処理すれば良くね?
他は既に突っ込まれてるから言わんが
例外に関するプログラミングしてたらすぐにわかる概念だからググれ
つうか例外安全って言葉使うなら知っとけ >>285
例えばprintfみたいな関数で下位の例外を処理するのか?
処理するとして何をやるんだ?
って話
考え方が逆なの、自分に関与しない例外は触らない
>>286
ライブラリならドキュメントに書いてあるでしょ
>>287
また呪文唱え始めたのかw
せめてこれに答えてよ
> そもそも最下位の関数に新しいエラーが定義されたらrust使いは全部調べ直すのか? >>289
> 例外に関するプログラミングしてたらすぐにわかる概念だからググれ
また無能のググれかよw
答えられないなら無駄に絡んでくるなよ >>290
別に調べ直す必要はないよ
下位にエラーが追加されても直接呼び出す関数のシグネチャが変わらないなら対応不要、変わったら対応するってだけ
結局呼び出す関数のResult型が対応すべきもののすべてなんだからそれ以外見る必要がない >>288
そういうCPUもメモリも浪費するエコでない言語との比較はほとんど意味がないんじゃないかな
GC言語であってもそこそこ速いJavaやGoくらいの立ち位置の言語ならば比較の意味があるとしても >>290
> そもそも最下位の関数に新しいエラーが定義されたらrust使いは全部調べ直すのか?
Rustでそんなことをする必要がないよ
元々他のエラーも返す関数だったならば返り値型が元々Result型だから枠組みは変化なし
新たにエラーを返すように返り値型が変わったならばコンパイルエラーで気付く fishシェルをRustで書き直すことが(ほぼ)決まったよ
https://github.com/fish-shell/fish-shell/pull/9512
C++ と CMakeをかなり腐してるけど意外に荒れてない
ちなみに提案者は開発リーダーなのでほぼ決まりでしょう
リンクされてる移行プランは他のプロジェクトでも参考になりそう >>292,294
> 元々他のエラーも返す関数だったならば返り値型が元々Result型だから枠組みは変化なし
なら、その新しいエラー(例えばディスクフルを検出してたが今回ディスクオフラインなんてエラーが追加された)の処理が抜けてないことはどうやってわかるんだ?
実行時にしかわからないなら例外と変わらん
むしろ途中の伝搬コードをいちいち書くのが面倒なだけだろ Elixir にも、try/rescue の例外があるけど、あまり使わない。
throw/catch, raise でも、例外の発生場所を関数の外側から内側へ移すだけ
try do
%{a: a} = map
{:ok, a}
rescue
MatchError -> {:error, ":a が無い"}
end
とは書かずに、パターンマッチで書くのがElixir流
case map do
%{a: a} -> {:ok, a}
_ -> {:error, ":a が無い"}
end >>297
修正。内側・外側が逆だった
>throw/catch, raise でも、例外の発生場所を関数の外側から内側へ移すだけ
throw/catch, raise でも、例外の発生場所を関数の内側から外側へ移すだけ >>296
それはResult<T,E>のEで返されるエラーenumのvariantが増えるだけ
んでvariantが増えればEをハンドルしてるところでexhaustiveに処理してなければコンパイルエラー >>282
>言いたいのは「似たようなエラーをまとめておいて、修正も一括で行う」のイメージだった。
これはResultのコレクションを返せばいい
例外のある言語でもこのケースは例外じゃなくエラー情報を貯めたオブジェクトを戻り値で返してエラーがあったかどうかをチェックして分岐するコードを書く
それと似たようなもの
>例えば「関数内で細切れでファイルに書き込んでいるとき、どこかで書き込みエラーが出たらロールバックしてログを取って再書き込みする」とか。
ロールバックする系の処理ならエラーを貯めずにエラーが一つ出た時点で中断するように作ったほうがいい dyn Errorで受け取った後の活用法が分からん dyn Errorでできるのは
・print!とかwrite!でログ出力する
(ErrorトレイトにDebugとDisplayが内包されてるからちゃんと実装されてれば何か教えてくれる)
・source()で内部のdyn Errorを掘り起こす
(エラーの原因のエラーがあれば一緒にログ出力できる)
くらいだからログ出力以上の活用がしたいならそのためのError型を使わないといけない >>302
具体的なエラーの型で分岐させたいならダウンキャストが必要(The Bookにも書いてたはず)
エラーenumを定義してwrapしたものに変換(map_err)しておけばダウンキャストは不要
anyhowに組み合わせてthiserrorの#[from]や#[error(transparent)]を使うと楽にwrapできる
anyhow/thiserrorのやってることに最初から自力で辿り着くのは大変だから先に使ってみて必要な要素を学んだ方が早いよ >>303
dynはErrorに限らず自由に元へ戻せるよ
例えばstd::io::Errorを含むdyn Errorが返ってきた時
if let Some(io_err) = dyn_err.downcast_ref::<io::Error>() {
match io_err.kind() {
io::ErrorKind::NotFound => {
このように細かいエラーハンドリングが可能
>>304
順序が逆だよ
まずは標準ライブラリ内で上記のようにdyn Errorを使ったり
あるいはdynを使わずにimpl From<MyError> for io::ErrorでMyErrorのEnumに格納する「?」時の自動変換を書いたり
それぞれ簡単で単純なパターンなのだから標準ライブラリで基礎を身に着けた上で自作や外部のライブラリを選ぶのがお勧め >>290
例外をサポートしているような言語なら、投げる例外を関数のインターフェイスとしてドキュメントに記載する。
Rustなら>>292
>考え方が逆なの、自分に関与しない例外は触らない
>290が>285 >286を理解できない無能だということは理解できた。
c++とかでライブラリ関数からドキュメントに無い例外を投げられた経験が無いんだろうな。おめでたい。 >> ライブラリならドキュメントに書いてあるでしょ
ドキュメントは言語システムの一部ではないため
ミスで現実のコードとドキュメントが食い違っていることもあれば
ドキュメントは正しくても利用者が見落としてしまうこともある
大規模な開発になればなるほどミスや見落としが紛れ込むことは避けられない
特にエラー処理が済んでいるか未だなのかは致命的になりかねない
一方でRustは言語システムの中で下位関数からエラーが上がってくるか処理済か分かる
さらにResultが未処理のままだとRustコンパイラが指摘してくれる
言語システムとして少なくともこれらの機能を持つ言語へと今後は移行していくべき流れ >>305
>それぞれ簡単で単純なパターンなのだから
実装の簡単さやパターンの単純さが問題じゃないんだよ
Rustのエラー処理に必要な「それぞれ簡単で単純なパターン」を網羅的に知識として仕入れてRustのエラー処理はこうやってやるものだと自信を持って言えるようになるまでの学習効率の問題
anyhow/thiserrorはそのパターンを楽に使えるよう作られてるから「ああRustのエラー処理ってこうやればいいんだな」ってのが標準ライブラリ前提で学ぶより断然早く理解できる >>309
dynの取り扱いと「?」オペレータによる自動変換はRustの基本事項で必須の知識
もちろん標準ライブラリの中で完結して使えるしシンプル仕組みなのですぐ使えるようになる
この基本を習得せずに外部ライブラリへ行くことを勧めるのは基本知識を欠いた人を生み出してしまう愚かな行為 >>310
もしかしてパターンってdyn Errorと?オペレータ使った自動変換の話だけなの?
それだけじゃRustで現実的にどうエラー処理を実装すべきかThe Book読み終えたくらいの人にはわかんないと思うよ >>311
エラー処理パターンは様々な方針があり
その前提知識としての共通の必須事項として今回のスレの流れで話が出て来ていたdynの取り扱いと?での自動変換の話を書いた
例えばあなたが出したanyhowはdyn Errorを扱う外部ライブラリの一種
anyhow::Errorから具体的な型を取り出すためには>>305で書いたdynの取り扱い知識が必須
この知識を知らないと細かいエラー分類が出来ずにエラー表示のみしか出来ない人になってしまう
もう一つあなたが出したthiserrorも?での自動変換を用いる外部ライブラリ(というかマクロ)の一種
>>305で示したFrom::from()による自動変換の基礎知識を欠いたままでは仕組みすら分からず魔法のように外部ライブラリを使うダメな人になってしまう
応用が効かないだけでなく利用していて何か問題にハマった時に基本知識がないと解決することもできない >>312
設計やプラクティスとしてのパターンのことを言ってたんだが君は言語機能のことをパターンと呼んでるみたいで噛み合ってないよね
dyn Errorや?オペレータ使ってinto経由で変換されるような基本的な機能面の知識はThe Bookにも書かれてるレベルだし多少分からなくてもリファレンス読めばいいだけの話
でもそれだけの知識でRust初心者がエラー周りの実践的な設計をできるようにはならないでしょ anyhow/thiserrorは最近出てきた新興ライブラリ
当然それまでは誰も使っていなかったわけで初心者がいきなり必須なものでもない
初心者にとっては複雑で機能過多で理解しにくいのでまずは標準ライブラリから始めたほうが良いかな 対象年齢付きのおもちゃか何か?w
自分だけ勝手に縛ってろw Rustは「教官付き教習車」だよ。
cとかと違ってアホが膝を撃ち抜く自由は許さない。
それが嫌ならRust止めれば? Rustはコンパイラの防波堤の中で安全な自由があるけどそれはともかく
クセの強いanyhowをRustの基本より先に初心者に教えるのはあかんよ
たとえば、他人も使うライブラリ作成ではanyhowの使用を避ける、といった当たり前のことも
エラーに関する基本を知らないと陥ってしまう >>316
rustの免許とりたいんですがおすすめの教習所はありますか?w >>317
あれはクセというよりanyhowの致命的な欠陥だ
分かってる人は配慮して閉じた環境だけで使うけど稀に公開ライブラリにanyhow使っちゃう無知な人もいる
基礎知識はホント一番大事 >>318
コンパイラ(教官)はひとつしか無いから選択肢は無いよ。 コードを貼るわけでも実装例を示すわけでもない時点でシッタカだろ anyhow勧めてる人自身があんまり理解してないんだと思う 複オジが使ったことないだけだろ
>>312も今調べてきました感満載じゃん
デファクトスタンダードになってるライブラリまで排除したらrustでは何もできんぞ オライリー本にも実戦ではanyhowとthiserrorを使えと書いてる 禁忌事項はライブラリを作って提供する時にanyhowを使ってしまうことだけだから
そこはanyhowを使わずにthiserrorを使えばヨシ 別にanyhowをthiserrorに差し替えるのも大した手間じゃないしな
ライブラリで使っちゃってるなら変更PRでも出してやれば良い >>325
anyhowとthiserrorを使うなと言ってる人は誰もいなくて
・まず先にstd::error::Errorを覚えよう
・次に?変換とdynダウンキャストを覚えよう
・そしてanyhowとthiserrorへ進もう
という話でしょ
そうすればanyhowをライブラリで使うべきでない理由もわかるでしょうし むしろRust基盤の一つ
downcastはエラー処理に限らずdyn Traitを元の型に戻すために必須
anyhowでも元のエラー型に戻すために必須 ダウンキャストのソース見てみたら
やっぱりunsafeのかたまりだった ダウンキャストを知らないと
>>303のようにdyn Errorで出来ることはログ出力だけと思いこんでしまう >>332
当たり前
Rustの標準ライブラリはunsafeだらけ
原理的にunsafeは避けられないからそれを安全なインターフェイスとして公開するのがRustの標準ライブラリ dynを元の型に戻すという発想に違和感(気持ち悪さ)を感じる人もいると思う
元の型に戻すつもりならそもそもdynにしないというか
ダウンキャストは戻すべき型(の変更)を全部把握できる閉じた状況じゃないと使いにくい >>335
anyhowを使うのはそういう全部把握できる閉じた状況で
独自エラー型を用意するのが面倒な時に使うからダウンキャストが理に適っている
嫌ならanyhow使わずにthiserrorを使えばよい >>335
型を全て把握しておく必要はない
例えばエラー処理で大半はエラー表示のみだが一部だけ特別な処理をしたいとすると
その処理したいエラー型のみダウンキャストして使えばいい
ちなみにダウンキャストは内部で定数のu64で表現される型番号をu64比較するだけなのでコストがかかるものではない ダウンキャストの扱いかどうか知らんが、パターンマッチングも総和型から個別型ヘのキャストみたいなもんだろ。 もしdyn使わずに自作Enum収容エラー型を定義して使っても
Enumタグ比較で分岐することになるから状況は似たようなもの std::any::Any.downcast_ref() を使わずにanyhow独自に実装してるね 初めて間もないけど
println!("{}", a);
とか手が攣って辛い
tabでもprintlnまでしかでないしコピペでもしてるの? downcastすればいいだけだからdyn Error返してもデメリットは無いってのなら
ライブラリでanyhowを使うのも大した問題じゃないって結論になるん? >>342
anyhowの問題点はそこではない
anyhowは最も重要なことを実装していない(不可能)という欠点があるため >>343
anyhowの問題点なんて一般的じゃないんだから、曖昧に言わずに具体的に説明しなよ。 予言しよう
ググれば分かるんだから書かないとか言って絶対に具体的な説明はしないやつだよ 俺はなんも知らんから頭の片隅に覚えておくくらいにしとくわ
使うときに調べる Rustでエラー型はstd::error::Errorトレイトを実装するのが共通のお約束だけど
anyhow::Errorは諸事情あって実装されていないんよ
std::error::Errorトレイト実装を前提として扱っているところへライブラリがanyhow::Errorを返しちゃうと困っちゃう
ライブラリを作るときはthiserrorでエラー型を作ればstd::error::Errorトレイトを自動で実装してくれるから大丈夫だよ
もちろんthiserror使わずに自分で実装してもいいよ >>348
「このライブラリはanyhow対応必須です。anyhow対応しないと使えません」
ならOKかね。 Box<dyn Error>もErrorトレイト実装してないよ(コンフリクトで実装できない)
その点はdyn Errorで返してもanyhowで返しても大差ない
libraryのpublicなAPIでtype erasedなエラーを返したいときは
Errorを実装した独自のエラー型を用意するのが普通
anyhowで返してる有名ライブラリもあるけどね
ライブラリでanyhow使ったらダメなんてことは特にないんだけど
Readme読めばすぐわかるように基本的にはアプリケーション用
なぜ同じ作者のエラー系のライブラリがanyhowとthiserrorと2つあるんだろうか?
と疑問に持つだけでも初心者はstdベースで学習するよりも1歩先に進めてる ダウンキャストは静的チェックできず変更に弱いからホイホイ使うものじゃないよ >>351
ダウンキャストは静的に型を指定して
Some()で静的な型で値が返ってきて
その値の使用も静的に型チェックされる
「マッチングが動的ではないか?」については
Enumで複数の型を収容した場合と全く同じ
どちらの場合もマッチングは動的に行われるが静的に型チェックされる
どちらも静的に型を抽象化した定数の整数値との比較によるマッチングとなる点も同じ Rustのdynとそのdowncastは安全性と最小限のコストを両立させており安心して使える
引数などでimpl Traitが使える場合はimplが有利なケースもあるが
返値などでdyn Traitしか使えない場合もあり適材適所で使い分け >>352
よくわかってないものを人に勧めちゃダメだよ ダウンキャストの安全性みたいなのは downcast_ref 使ってりゃ議論の余地はないと思うけど網羅性は限界があるわね。
ライブラリはやっぱエラー型を明示すべき。 エラー処理で網羅性は関係ないだろ
std::io::Errorのenum ErrorKindからしてnon_exaustive指定だぞ
match式で網羅性はチェックされない
特別な処理が必要なエラーだけ処理対応して残りはエラー表示が普通だ エラー処理で重要なことは
・エラーが発生しているにも関わらずそのまま通常処理を進めないこと
・エラー表示以外の対応を必要とするエラーの場合にその対応をすること
・それ以外のエラーはエラー表示などをすること
enumタグレベルの網羅性を求められることはないな 元々の話はdyn Errorかanyhowか、だったような気がするけど、
anyhowはdyn Errorの自作ラッピングみたいなもので、
とちらを使ってもダウンキャストしなきゃいけない点も網羅性がない点も同じだよね。
そしてそれらをアプリ側で使ってる限り困ることもない。 そもそもがダックタイピングとか向いてる言語じゃない。
フロントでこの言語使おうとするとかただのバカでしかないわ。 唐突にフロントとかダックタイピングとかどうした?
しかもその二つも関連性がない 上位レイヤーも含めてエラーの内容によって分岐したいならdyn Errorはやめたほうがいい 分岐にenumのタグを使うかErrorの型名を使うかの違いでしょ
enumを使うと一貫性を担保しやすいから保守性が向上するし
dyn Errorを使うとenumの定義を省略できるからコードを減らせる
自分は(分岐するなら)enum派だけど何を重視するかで結論が変わりそう
ただ「やることが一緒だからどっちも同じ」と考えはいただけない >>364
dyn Errorをやめるべき理由がない
まさかと思うが代わりにanyhow使えとか言い出すんじゃないんだろうな? dyn Errorもダウンキャストも使用して全く問題ないよ
有名どころでも普通に使われている
例えばreqwestはdyn Errorを使っていてdowncast_ref()してエラー処理している
cargoはanyhowを使っていてdowncast_ref()してエラー処理している
使うのをやめたほうがいいと主張している人は一種の宗教にすぎないことに気付こう 勘違いしてる内容が同じだから
自演しまくっても同一人物なの丸分かりだね
ダウンキャストオジ==複オジ 一年ぐらい前にanyhowを標準化する、みたいな話なかったっけ? オライリーの本って出てから1年経つけどさあ
これの電子版って、セールになったりする機会ってあるもんなの? >>359-360
おまえはまるで何もわかってないのな
エラーハンドリングの基礎すらおさえてないじゃん
無知なのはいいが自信満々で嘘を書きまくるのはやめろ 作るシステムの性質や分野ごとで許されるエラーハンドリングは別物だからね。
ミッションクリティカルだけが正解ってわけでもないのよ。 >>375
何がまちがってるかさえも分からないようならThe Bookを一から読み直して出直してこい たまに出てくる「間違っている」「勘違いしてる」「嘘を書くな」の人
今までも文句をつけるだけで正確を書いたことがないから信頼できないのよね どっちも自分の脳内シチュでしか語ってないからふわっふわで論破出来るわけもなし
自分の主張が通る具体例上げるといいよ 「カッコウのアルゴリズムさえまともに実装できてないからクソ遅いんだよな」
みたいに適当なワード混ぜて意味不明なこと言いつつ同意してる風を装うと「そうなんだよ!」とか勝手に乗っかってきて自爆するから面白いぞ >>377
それは信頼以前に、相手にしなくていい外野
>>379
すごく目に見えるわその展開w >>373
台所まで行けば箸(enum)があるのに手元のフォークで焼きそばを食べてる感じ
食べてるのがパスタ(Python)なら平気なんだけど >>381
微妙にわかりにくい例えで草
でも複オジに比べると1000倍マシ >>377
信頼するかどうかはあなた次第
>>359-360が嘘だらけだということが分からない人は基礎がなってないから勉強やり直すかパスタにするか 網羅性が必要ないならResult使う必要もRustを使う必要もないわな >>381
凄い納得
enum使わずにanyhow使うような連中はPythonでも使っていろ
anyhowなんてものがあるから勘違いが起こる cargo crateのコード見てみた
anyhowを使ってエラーを上へ上げていく
enumを使っておらずエラーの網羅性はない
downcast_refを使ってエラー分岐している エラーなんて作成時点で未知のものが後から増えることもザラだし
何なら抽象レイヤー以下の実装の差し替え(ストレージのネットワーク化とか)で
動的に増えたりすることもあるんだから、網羅性とかすぐ成り立たなくなる。
可能なところで逃すものじゃないけど、こだわるものでもない。 cargo みたいに手元や CI で実行するツールのエラー処理なんてその程度で十分ってこった。
もちろん、網羅が必要な領域のコードじゃそんなエラーハンドリングのやりかたじゃあ駄目だろうよ。 多数の下部ライブラリから色んなエラーが上がってくるreqwestはそれらをdyn std::error::Errorへ入れているね
そしてdowncast_refで必要な分岐をして処理しているね
エラー処理はこれで十分でしょ
網羅しなくても残りは最終的にちゃんとエラー表示されるのだから エラー処理で必須な網羅性とはenumの網羅性とは異なる
いつどこでどんなエラーが発生しても必ず処理されることがエラー処理の網羅性
つまりエラー処理されないまま通常処理に進まないことでありRustではResultの利用によりそれが保証されている
したがってdyn Errorを使って一部をダウンキャストによりエラー処理し残りをエラー表示処理でも網羅性を満たしている opaqueなerror typeを使うメリットは何なのか?デメリットは何なのか?
少なくともreqwestの開発者はそのトレードオフを検討した上で判断を下している
メリットもデメリットも理解できてないやつが珍説唱えても虚しいだけだぞ >>390
おまえはエクストリームな言い訳並べ立てる前にnon_exhaustiveの意味から調べろな >>390
やっぱりそれでいいのか
Rustの標準IOライブラリはenumを使っているけどnon_exhaustive指定がしてありenumの網羅性チェックをむしろ使えないようにしてる >>390
一般的なエラー処理の網羅性はその解釈だな
Rustでは型システムと返り型Result及びResult放置をコンパイラが警告してくれるため必ず網羅される仕組み >>393
non_exhaustive指定があると
なぜenumの網羅性チェックが使えないの? 逆だよ
網羅性の扱いを禁止するために意図的にnon_exhaustive指定されている
そのようなエラーの種類には網羅性は必要がないだけでなく
今後もしエラーの種類が増えた場合に全網羅列挙して使用している既存のコードがエラーとなってしまう
そのためこういう時は不要である網羅性の扱いを禁止して全列挙コードを書かせないことで
今後エラーの種類が増えても既存の利用コードに影響を与えずに済む仕組み >>396
自信満々に間違った情報を中身のない文章で膨らませてもっともらしく見せようとするところがChatGPTそっくりww 昔ながらの例外論争見てるみたいだ
全部取ればいいいや個別だ >>396で合っているけど補足すれば
全網羅列挙コードを書いてもコンパイラがそれを許さずエラーとなる
全網羅列挙しなくていいから必ず全マッチの _ => アームをコンパイラが要求してくる
つまりコンパイラによるエラーの網羅性チェックは不可能 そもそもRust以前に他のプログラミング言語でもそのようなエラーの種類の網羅が行なわれたことはない
IOエラーだけでも何十種類もあり列挙するだけでも大変で意味のない作業
エラー処理で必要とされる網羅性とはエラーの種類を全て列挙することではなく
>>390の説明が正しい
RustでResultを使えば保証される 自分の書いたレスを正しい正しいと
一人で連呼してて虚しくならないのかな エラー処理の網羅性という話は元々
関数からエラー値が返って来る可能性があるのに見落として処理せずに続行しちゃったり
あるいは例外が返って来る可能性があるのに見落として処理しないままでいたり
そういう見落としがよく起きていたから
いつどこでどんなエラー(or例外)が起きても処理漏れがないようにプログラミングしましょうという話
もちろんRustではResultで返せばコンパイル時点でエラーや警告が出るためエラー処理の網羅性は満たされます
Resultを返していればdyn Errorやanyhowを使っていてももちろん大丈夫
そしてエラー処理の分岐にそれらのダウンキャストを使うのも何ら問題なし anyhow使ってて困ったことないので、これからもanyhow使いますね enumで網羅派の完全敗北だな
anyhowやdyn Errorでdowncast_ref分岐して何ら困らない 下手に分かったふりせず疑問をぶつけるパニック君のようなレスはスレにとっては有益
逆に知ったかぶりして嘘を撒き散らす某オジのレスは害しかない
一緒に仕事してても伸びるのは断然前者のタイプ
後者は多少知識があってもチームの足を引っ張る老害タイプ >>406
知ったかぶりもだけど
ご飯論法的に毎回論点を捻じ曲げてくるのが悪質
スルー推奨 どの言語を使っていようがテスト設計とエラー処理設計は初心者と脱初心者を見分けるリトマス試験紙
チュートリアルやリファレンス読むだけでは身につかない >>409
こういうバカが一人でも入ってくると現場は苦労するわな 今回のケースはRustのエラー処理を他の言語(C#あたり)の例外処理に寄せようとしてる感じ
「特定の例外クラスをcatchしたい」→「dyn Errorで投げてダウンキャストで分岐すればいい」みたいな
anyhowはそういう人のためにあるのかもしれない
オブジェクト指向言語専攻の人はRustのtraitを基底クラスの代わりに使いがちだよね
is-a継承はRustだとenumで表現できる場合が多いんだけど切り替えが難しいのかも 単純にエラーの種類を列挙する意味がないときに使うだけやで それならいいんだけど型キャストしてまで分岐するとか言ってる人がいたから
自分で分岐する範囲くらいは自分で列挙しとけって話 下層から上がってくる99種類のエラーはログ吐かせるだけで、特別な処理したいエラーはひとつしかないのにいちいちenumに詰め直したりせんわな 逆
自分で分岐してエラー処理すんなら区別すべきエラーを決めるのは自分なのでanyhowやdyn Errorでダウンキャストしても問題ない
外から呼び出されるコードなら区別して処理するエラーを決めるのは呼び出し側なので区別できるようにenumで書く すまんが、これってなんでダメなの?
ブロックの最後に式を書いてるのに、なんでreturnって書かないとリターンしないの???
そもそもエラーの内容に書いてあるmismatched typesって、一体何と何のタイプがミスマッチなんなんだ・・・・
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d1833710038ed821593015e5382de11f
エロい人教えて!!! >>416
else句が無いのでifの条件式がfalseの場合はif-expressionは()になるけど
ifの条件式がtrueの場合はif-expressionがi32になるのでミスマッチ >>414
ログを吐かせるだけでもエラーによってログの吐き方を変えたいこともよくある話なのでケースバイケース
99種類を2種類に詰め直して片方をerased typeにしたりする 上位の関数でもかなり多数のケースに分けてエラー処理する必要な場合だと
個別にenumにエラー型を収容する方が有利なケースもある
もちろんその場合でもanyhowやdyn Errorに収容してダウンキャストしてもよい
enum matchとダウンキャストの実行時コストはほぼ同じで優劣はなく視認性も変わらない
enumを使うとコードが増える問題はあるがあとは好みの問題 ダウンキャストは下位モジュールに不必要に依存することになるので選択の余地があるなら基本的には使うべきではない
自crateで定義したエラー型にダウンキャストするくらいならエラーを定義するついでにenum書いとけばいいだけ
他crateで定義したエラー型にダウンキャストしてるとバージョンアップ時にサイレントにキャストが失敗して動作が変わる
あくまで非常口であって使わざるをえないときはマイナス面を理解した上で注意深く使うもの UnknownErrorにわざわざ詰め直したログ吐くしかできない99種類あったエラーと1種類の自クレートのMyErrorをenumで2つ列挙して分岐するのと
MyErrorも含めて100種全部dynで上げてMyErrorだけダウンキャストすることの間に大した違いはない アプリケーション開発とライブラリ開発の2つの視点があって、それぞれで戦略が違うというのが大元にあるからそこは理解してもらわないとね。 ライブラリでdyn Error使うと本体の型がpubじゃないときにめんどくさそう 両者で大きく異なるよね
ただし既出のcargoやreqwestなど違いに関わらずダウンキャストを使っているコードも多いから
どっちだと良くてどっちだとダメというものでもない気がする 424は422へのレスね
>>423
そこはenum作って収容しようがdyn Errorに収容しようが関係なくpubじゃない型はどうにもならないような >>421
どういう種類の変更に対してコードのどの部分に手を入れる必要が出てくるのかを考えてみるといいよ 求められてもない具体性のない中身のない全く役に立たないアドバイスは有害でしかない 現実にdowncast_ref()が使われているアプリやライブラリが多数あり
それらが問題になったことはないのだから
downcast_ref()を嫌っている人は思い込みで好き嫌いに過ぎないことを理解しようぜ
enumを作っても下位のエラー型を突っ込むだけでは何も変わらない 前提条件付けて場合分けして考えろ
てかこんなことでスレの半分近くも消費すんな >>417
そうだったのか!returnを書いたら通るのも、そうするとどちらも戻り値が()同士で統一されるってことか!
ありがとう!全然わかんなかったぜ! >>417
>>416では無いが、そういう意味だったのか
単に厳し目にチェックしているからだと思っていた たぶんだけど「Rustでは値を返すのにreturnを省略できる」と誤解してるんじゃないかな
その誤解のためreturn bのreturnを省略してしまったと思われる
正しくは「Rustでは最後の値が返り値となる(のでreturnが不要)(だが使ってもよい)」
と「途中で返したい時はreturnを使う」
だから今回の例だと
if文の中では『途中だから』return bとbにreturnが必須
あるいは関数の最後をif-else式にしてしまえば『最後となるから』bにreturnは不要 ちょっと誤解があるから追加
最後をif-else式にすれば全体に式returnのreturnが不要になる
そしてif-else式の中はreturnは不要というか付けない
そこにreturnを付けるとif-else式ではなぬif-else文になる return式の型は厳密には!なんだけど……
まあ後で知ればいいか そのおかげでif式でreturnしつつelseで値を与えたりできるね >>439
昔はそう言われたけど今はそんなカス風習廃れたしRustに持ち込まないでほしい。 早期returnは(>>439みたいなアホが使うと)バグにつながるから禁止が常識 わざわざ早期リターンのために?演算子なんて用意してるRustで早期リターン禁止とな?! なんで昔は早期returnがバグにつながると考えられてたの?
どう考えても早期returnしない方がバグにつながりやすいと思うんだが もっと昔にgotoの使いすぎで制御構造がぐちゃぐちゃになったことへの反動で
複雑な制御構造は使わないようにしよう、という流れだったかと >>444
C言語時代の話な
例えばこういうパターンでメモリーリークしたりするバグがたくさんあった
f(...){
p = malloc(...);
...
if(...) return;
...
free(p);
} そのあたりも自動メモリ開放のRustなら大丈夫だね
if文で早期return/continue/breakしないと、どんどんifネストが深くなっていくのでそれを避けたい >>446
リソース確保・解放するレイヤーとそれを使って処理を行うレイヤーを分ければよくない?
f(...){
p = malloc(...);
g(p, …);
free(p);
}
g(…) {
...
if(...) return;
...
}
ifのネストが浅くなっても逆に関数のネストが深くなりすぎて読みにくくなる側面があるということなのかな? >>448
分けましょうってルール化したくらいで完璧に守られるなら誰も苦労しないんだよな…
そのうち処理レイヤー側でmallocするやつが出てくるやつ ifがネスとしていたりした場合に、returnみたいにif-expressionの方に結果を返したい時ってどうすればいいの? >>449
早期returnはやめましょうルールは完璧に守られる保証があると? >>451
擬似コードでいいのでイメージを書いてくれ
でないと言いたいことが分からない 言語仕様的に可能でも規則で縛ることで安全を担保しようという方法は
いずれ失敗するからそういう要素は必ず言語側の仕様で保証すべき、というのがRustの思想なんだから
「あれは危険だから駄目、コーディング規則で縛る」という発想自体が間違ってる >>448
リソースが1つだけならいいかも知れないけど複数リソースが絡んでたりすると色々ややこしいコードになっちゃう
>>452
コードレビュー時に return 検索したら >>456
なるほど
そういう方法だけに頼ってるなら早期returnのほうが見つけやすいわな こういうやつ?
let result = 'found: {
for i in 0..100 {
if f(i) {
break 'found Some(i);
}
}
None
}; >>453-454
>>459
やりたかったのこれだ、せんきゅー! 最近はCでもcleanup属性というのが使えるんだな >>444
構造化が重要視された時代がある。
順次・反復・分岐によって処理の流れを構築するという点から見ると
早期リターンは流れをすっとばして大ジャンプしてることになるので
コードを追うのを難しくすると考えられていた。
「出入口はひとつずつ」は基本思想だったんだよ。
構造化が不十分だった時代にまず構造化することが大事だったという話。
今では構造化が当たり前になったから次の段階として順次・反復・分岐という構造だけでは
上手く扱いづらい部分をどうやるのがいいのかという模索が続いてるわけ。 戻るgotoは結局なんらかのループを表しているので
Rustならラベル付loopに指定continueで表せる
進むgotoは無制限ではなくラベル指定breakの大域脱出型に制限される
という認識で合ってる? >>454
なんか微妙な機能だな
関数化して早期returnさせる手間が嫌ということなんだろうけどそれなら||や&&オペレータをOptionやResult用に上書きできるようにした方がずっと使いやすい
tryブロックとの非対称性も気になる >>464
今までに既にあった機能のラベル付loop式のラベル指定breakという使われ方のうち
ループしない使われ方が多かったため事実上のシンタックスシュガーが導入されただけだよ
だから微妙な機能が追加されたという見方は正しくない >>464
ほとんどの場合はこんなん使う前にその部分を関数に切り出すことを考えるべきというのはめちゃくちゃ言われてる
awaitとかResultとかライフタイムとかいろいろ絡んできたときに再利用もされない数行の処理のために長々と型を書かなくても済むのがユースケースのひとつと思われる >>465
シンタックスシュガーも機能やろがい
公式にもlanguage featureと書いてるぞい どうせなら仮変数みたいな変数割当機能も用意して、末尾最適化再帰風ループもできるようにならないかね。 >>467
既に昔から存在していたloop式を
loopと書かなくてもよくなっただけだよ
今までは皆はloop式の最後をbreakすることでloopさせずにこの同じ機能を使っていた
今回でloopを省けるようになったため最後のbreakが不要とだけにすぎない
今さら文句をつけるのが不思議 早期returnってファウラーのガード節の話じゃないん?
あの使い方する限り安全安心可読性改良にしかならないよな? >>470
ファウラーじゃなくてリーダブルコード? if letでネストが深くならないように早期return等しようとすると無駄コードが膨らんでいた問題も
同時に導入されたlet elseによって解決されたね >>472
そうこれ、というか第1版からあるならファウラーが先なのね
失礼した 「ルールだからダメ(思考停止)」な人って結構いるよね ルールの背景を考えずに破ろうとするやつも多いけどな。
守破離は基本だろ。ブルースリーのパンチキックの逸話でもいいけど。 >>476
合理的なロジックがないという点で同類じゃね
「ルールだから問題ない(思考停止)」で他人に迷惑をかけるのも同じ
そして多くの場合無責任までセットだ そもそも失敗できなくするのがここ10年ぐらいのトレンドだろ。
だから静的型やRustが支持されるわけで。 >>477
ルールについては「決定と実行の分離」という重要な側面があるよ。
頭のいい奴がルールを作れば、アホがルール通りに実行しても被害は少ない。アホがルールを破るよりよほどマシ。 Rustの制御構文としては
昨年末の2つの追加で
従来は不便で需要が多かった形が無事に解決したから一段落かな 草
何らかの目的で大域脱出を用意すると必ず妙ちくりんな悪用を考える奴が出てくるんだよな そういえばコンテナのイテレーションの順序が未規定なときに
順序に依存したコードを書かないように乱数でわざとデタラメな順序にしたら
乱数生成器として使うやつが出てきたって話があったな。
どんな機能も下手に使ったら無茶苦茶にしうるってのはもうどうしようもなくない? >>56のが実現したらRFCの例は||で代替可
let result = (first_container.iter().find(|&&v| v > 0)
|| second_container.iter().find(|&&v| v < 0))
.unwrap_or(&0);
labelやbreakの手続き的なやり方よりこっちの方が汎用性も高いしrustの目指すべき方向に合ってると思う メソッド抽出は嫌
クロージャの即時実行も嫌
ラベル付きbreakならOK!
って感覚が俺には理解できんが
ニーズがあるならいいんじゃね >>486
関数切り出しやIIFEだと式の値はResult型になるようなところで、
ラベル付きブロックだとエラーまわりは親の関数に完全に任せて該当の式の値を生で返せる ラベルでジャンプより、関数の入れ子の方が分かりやすくない? >>487
?オペレータ使えばいいだけなんだが?
逆にエラーまわりだけ親関数に任せるとかヤバくね? >>488
ラベルでジャンプしていない
loop式の構文のloop抜きと動作も概念も全く同じだから
今回Rustはたまたま同じ構文を採用しただけ
例えば>>459の例は
ラベルを用いない別の新たな構文{{…}}を導入すればこうなる
let result = {{
for i in 0..100 {
if f(i) {
return Some(i);
}
}
None
}};
動作も意味も全く同じであり
ラベルにジャンプしていないことが理解できるだろう そしてRustがなぜ今回の構文の形を採用したか
・returnでもbreakでも多重になった時にどれを終えるのか曖昧になるため何らかの指定は必要
・ラベルによる指定はRustの他の構文でも共通に既に使われており親和性がある
・returnの使用は混乱を防ぐために関数とクロージャーから返る位置付けのみに保ちたい
・既存のloop式と今回の構文はloopキーワードを除けば全く同じであり導入前はloop式で代替されていた >>488
必要性に応じて関数として切り出すのも構わない
そして関数として分離した方が好ましい場合もあるだろう
しかし常に関数切り出しを強いられる言語だったとしたら煩雑すぎて機能不足と言えるから使い分けできることが必要 >>493
従来からforやloop式で使われてきた早期return(離脱)つまりRustでのラベル指定break
そして今回Rust導入されたブロック式での早期return(離脱)つまり同様に構文としてはラベル指定break
それらに対して「ラベルでジャンプ」とは何を言いたいのか? >>485
短絡orに論理orを使うのは混乱の元だから、別の専用の記号を用意して欲しいわ。 >>495
遅延評価orの型が
現在はbool型のみに制限されているから
それをOption型などにも使えるようにしよう!という話じゃないかな >>489
関数に切り出した時点で「その範囲のResultの型」を考慮しなくちゃいけなくなるから?演算子では何も解決してないし、
切り出さなきゃ元からその関数の処理だから何もやばくないぞ 関数切り出しすると
その中でも継続して使う変数(値)を参照として全て渡してそれらの型宣言も改めて行なってと煩雑さとコードの重複が大きい
もちろん返り値型も型推論で済まなくなり明確に宣言しなければならなくなる
したがって関数切り出しの方が明確にメリットが上回るケースを除いて
適材適所でブロック式で済ませられるようになった今回の導入は大きな意義があると思う >>490
動作も意味も全く同じとのことだが
その構文でネストしたブロックから抜けられるのかい? >>495
今も||は「短絡論理OR」で区別されてないよ
短絡論理ORと論理ORを分けてる言語ってある? 5年もの歳月を費やしてstabilizeするほど意味ある機能じゃないよな
超ニッチなnice to haveなんだからこんな機能実装するくらいなら他の作業してくれよ >>499
Rustで関数のネスト定義は可能だけどスコープを親関数内に限定するだけだよね
親関数の変数をもちろん操作できないので改めて引数も戻り値も全て型宣言して受け渡ししなければならない >>499
関数のネストでやるためには
関数に切り出す必要があるから同じことを言ってるんじゃない? >>500
forやloopを使ったときにどのbreakか区別できるようにラベルが付いてるだけでラベル付ブロック式のネストまでは元々求められていないんじゃないかな
たまたまラベル付となったからネストさせて指定して抜けることも可能なのかも知れないけどさ >>505
指定したラベル位置に制御を移すような動きのことを一般的にはジャンプと呼ぶ
ラベルで指定したブロックを”抜ける”と呼ぶ感覚ももちろん理解できるが同じようにジャンプと呼ぶ感覚くらいは理解してやれって話 >>502
ブロック式の早期離脱機能は同時に導入されたlet elseと並んでRustにとって必須の機能
これまではブロック式で早期離脱が出来なかったため代替処置として
・ブロックの代わりにわざわざクロージャを用意して即時実行する
・ループさせないのにloop式を目的外でブロック式として用いる
・関数として切り出して引数型や戻り型などコードが無駄に膨らむ
以上3通りの方法が苦しい回避処置として取られてきた
この問題が一気に解決した >>507
おまえホント壊れたレコードみたいだな
馬鹿の一つ覚えで繰り返し同じことしか言えないからおまえが出しゃばってくるとすぐスレが腐る オジさんは即時実行マンセー派だったのに宗旨替えしたのかw
と言っても忌み嫌われてた即時実行から半歩前進して半歩後退してるみたいなので実質進歩してないな 誰もが必ず使うエラーまわりには散々ボイラープレートを要求するくせに
ブロックからの早期returnが必要なくらいの処理を関数に切り出したくないという理由だけで
プチgoto入れちゃうのはバランス感覚おかしいわな ブロック式も関数も複数の文をひとつの式にするものだから関数から早期returnできるならブロック式から早期breakできてもええやん😁 returnやbreakのことをgoto扱いしてる人は頭おかしい
むしろgotoを排除するために現在の言語ほとんどに備えられている
ラベル付breakをサポートする言語も多い Rustの機能はすべて素晴らしいということにしないと気が済まない人がいるのはよく分かった Rust の機能が全て優れてるとは思わないけど ラベル付き break なんて揉めるような機能じゃないだろ... Rustに限らず早期return、早期breakはどの言語でも要 ブロックからの脱出とジャンプを同列に考えるやつは勉強不足だと思うがね。
Rustのスタックフレーム志向とかを考えれば、ブロック出入り操作を重視するのは自然。逆にスタックフレームの局所性を破壊する例外フローとかジャンプは嫌われて当然だわ。
例外フローにおけるエスケープ解析て確立しているんだっけ? 規約で禁止していいレベルの機能
Avoid labelled break. Use functions instead. >>508
唐突に発狂されても意味不明
反論があるなら技術的な話でお願いします >>520
関数みたいなブロックを作ればいいんじゃない?
あるいはブロックみたいな関数とか。 >>520
関数みたいなブロックを作ればいいんじゃない?
あるいはブロックみたいな関数とか。 >>520
Rustには何年も前からラベル付breakがあります
今さら文句をつけている人はRustを知らずイチャモンを付けたいだけだとバレていますよ
メジャーなプログラミング言語の大半がラベル付breakを備えています
JavaでもSwiftにもGoもJavaScriptすらラベル付breakを持っています
プログラミングにおいて必須の機能だからです
逆にラベル付breakを持っていない代表的な言語がC/C++です
今回のRust叩きをしている犯人はいつもと同じ人だと分かります breakに対してジャンプだとかgotoだとかトンデモ発言が出ていたのはそういうことか
C/C++しか知らないと多重ループすらgotoで抜けるしかないもんな
そういう貧弱な世界しか知らない視野の狭い人だから必死にRustを叩いていたわけか 前から常駐してるアンチRustのやつだろ
ばればれだがアンチとばれないように装いつつRustの色んな点を批判してきた
特にRustならではの機能とか新たな機能とかRustらしい書き方とかを嫌う >>524
大半の言語が備えてるラベル付breakはループから抜けるものでブロックから抜けるものじゃないぞ swiftがdo statementでブロックスコープから抜ける機能を用意してるがtry/catchなしの形では誰も使ってない そんなことよりさっさとif let Some(c) = chars.next(); c.is_whitespace() {とかって書けるようになってほしい ブロックは「必ず1回で終了するループ」と解釈可能なのでループからのラベル指定breakがあるならブロックからもbreakで抜けられる方が自然 Javaも文法的にはループ以外でもラベル付きブレイク使えるけど絶対使わないな >>531
その理屈でいけばラベルなしブロックからもbreakで抜けられないとおかしくない? RFCの例を4パターンで書いてみたけどラベル付きbreakは無いな
1か2のパターンに収まる形にリファクタリングする
大半の人がラベル付きbreakを選びたくなるようなサンプルはないのかな?
1. 関数化
2. Optionコンビネータ
3. クロージャ
4. ラベル付きbreak
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e365b15243be65f5fc2d09a94324317e 流れと一切関係ないけど
rubyは一時期tapとbreakで値を返す文化?あったな
foo.tap {|x| bar} // selfつまりfooを返す
foo.tap {|x| break bar} // breakでbarを返す
foo.then {|x| bar} // ブロックの結果つまりbarを返す。今はthen使うのが素直。 >>530
セミコロンじゃなくて&&だよね
stableになるのは半年後くらいじゃない 異なる種類のエラーを返す関数が絡んできたとき、ラベル指定breakのみエラーの合成を?演算子で関数境界に押し込められる
関数やクロージャだと「ブロック内のエラーを合成した型」を明示的に取り扱うことになって二度手間 >>535
tapでbreakなんて初めて知ったわ
イテレータのメソッドチェーンの途中経過を出力する時にしか使ったことなかった >>537
これは同意
>>538
サンプルコード書いてみてよ >>541
詳細はトレイトとか Deref とか IntoIterator とか関連型とかその章ではまだ出て来てない概念が色々絡むが
for item in v の v に対して
Vec<T> を渡したら item は T になって所有権ごと持ってく
&Vec<T> を渡したら item は &T になって不変参照のイテレーションになる
&mut Vec<T> を渡したら item は &mut T になって可変参照のイテレーションになる
Vec<T>は DerefMut<Target=[T]> を実装しているから &mut Vec<T> は暗黙のうちに deref_mut のメソッドを通して &mut [T] に変換されて
その &mut [T] が IntoIterator<Item=&mut T> を実装しているからfor文で &mut T のイテレーションができる >>534
その程度ならば型宣言で冗長になる関数化をするまでもないからブロック式でも十分かな
使い分けできるようにブロック式の不備を整備したRustの方針は正しいと思うよ
>>541
所有権を渡すのは消費尽くすときだけでその後も使いたい時は参照&か可変参照&mutを渡す
そのコードのv.iter_mut()の方に見かけ上&mutが見当たらないのはメソッド定義にそれがあるから
メソッドでは所有権を渡すか参照を渡すか可変参照を渡すかをその定義でselfか&selfか&mut selfか指定することでメソッド使用時に毎回指定しなくて済むようになっている >>541
vがVec<T>の場合
for i in &mut v {…} と
for i in v.iter_mut() {…} は同じ
前者は&mut Vec<T>のIntoIteratorトレイトのinto_iter()が呼ばれる
その中身はself.iter_mut()なので後者と同じになる
for loopはiterable(IntoIteratorを実装してるもの)を受け取って
そのinto_iter()を呼び出してからイテレートする仕組み
https://doc.rust-lang.org/std/iter/index.html#for-loops-and-intoiterator >>542
>&mut Vec<T> は暗黙のうちに deref_mut のメソッドを通して &mut [T] に変換されて
細かいけどここちょっと気になった
for loopに渡されたタイミングでDeref Coercionが発生するんじゃなくて
まず最初に渡された値の型でIntoIterator::into_iterが呼ばれて
その中のself.iter_mutのようなメソッド呼び出し時にVecに該当メソッドがないから
Deref Coercionが発生してslice::iter_mutが呼ばれるという処理順だと思う >>537
処理に名前を与えることがいかに重要かよく分かるいい例だな なるほど
処理名をラベル名として付与するとラベル付ブロックが非常にわかりやすくなった
関数化と異なり無駄な引数宣言と型宣言が不要となり可読性が増した
もちろん関数化した方が適してる場合もあるだろうから上手く使い分けると良さそう さすが汚コーダー!
可読性や保守性になんて目もくれない!
保守なんてやらないもの! >>516
それな!(Noneでこんな感じにしちゃったんだろう、コール側がこうである必然性があまりない); 1回しか使わない小さな関数in関数は関数名付きブロックで十分やね 外側の関数で既に変数定義してるのに、再び内側の関数の引数で宣言するのが二度手間だったから、ブロック化いいかも。
特に参照している変数が多いと連れ回しと、型宣言が大変だった。 IIFEとかいう💩よりはラベル指定ブロックの方がいいのは間違いない 色んな有名クレートのソース見てると
let result = (|| {
…
})();
の形も多いね >>552
承認欲求を満たしてあげないとbreakしない
嘘でいいから同意して差し上げろ try_blocksが安定化されればここまでの話全部吹っ飛ぶんだけど
それまでのつなぎでしかないコードでここまで議論を紛糾させる必要なんてないのよ >>556
もちろん代替として今は>>554の形で使ってるよ こんどはそれでラベルbreakでええやんって議論が巻き起こるんでしょ >>558
一部だけ用途は重なるけど互いにできないことがあるからtry blockとlabeled blockは併用になる >>556
try ブロックとラベル指定ブロックて用途違うんじゃね
let result = 'found: {
for i in 0..100 {
if f(i) {
break 'found foo(i)?;
}
}
bar()?;
}; 途中送信
>>560 のケースで
foo(i) が Result<i32, FooError>
bar() が Result<i32, BarError>
を返すとして、ここでFooErrorとBarErrorの合成を考えなくても関数境界にエラー合成を押し付けて、
result の型を i32 にできるのがラベル付きブロック
クロージャとかは?演算子のスコープを作ってしまうので一旦Resultの型を明示しなきゃいけなくて、
FooErrorとBarErrorの合成をロジックの途中に挟むことになってうれしくない >>556
それはエラーの早期離脱のためだから、普通の値の早期離脱には使えない。
break可能なブロックは必要でしょう。 >>562
従来のif式やmatch式と同様に、tryでないblock式では?を通過してくれるところが嬉しいポイントかな。
一方でtry block式はTry実装のResult型などになってしまい別物。 >>560
書き直せばいい
let result = match (0..100).find(f) {
Some(i) => foo(i)?,
None => bar()?,
}; >>566
その人じゃないけど
tryとの違いを示してる簡素な例にその反論は意味ない
そして多重forの一般形となるだけでそのやり方は破綻 >>567
なんで破綻するのかよくわからない
コードで示してくれ 破綻するかどうかは別として
多重forの中で種類の異なるエラーを返すような処理を直接書くことにはすごく疑問を感じるから
本当にそういうやり方が必要な状況があるなら見てみたいね 単なる多重forショートカットの話だよ
とりあえずエラーや可変長や可変参照問題は抜きにシンプルで
let n = 'calc: {
for i in 0..100 {
if f(i) {
break 'calc ff(i);
}
for j in 0..100 {
if g(i, j) {
break 'calc gg(i, j);
}
}
}
0
}; ラベル付きブロック式は特に新しい挙動は導入していない
早期脱出と?のエスカレーションの併用が可能になっただけ
関数:
〇変数、ライフタイムのスコープ
×環境のキャプチャ
〇早期脱出
×?演算子のエスカレーション
クロージャ
〇変数、ライフタイムのスコープ
〇環境のキャプチャ
〇早期脱出
×?演算子のエスカレーション
無名ブロック式:
〇変数、ライフタイムのスコープ
〇環境のキャプチャ
×早期脱出
〇?演算子のエスカレーション
ラベル付きブロック式:
〇変数、ライフタイムのスコープ
〇環境のキャプチャ
〇早期脱出
〇?演算子のエスカレーション >>571
?使わないならクロージャでいいパターンでは?
あとそろそろfやggじゃ抽象的すぎてきついから実戦で使いそうなパターンの例を頼むわ むしろこの用途でクロージャでIIFEする方がラベル指定breakより糞だよ >>573
クロージャ即時実行だとできないこともある
>>575
なるほど
引用
>ラベル付きブレークはループ構造にすでに存在していたため、これは独自の新機能ではなく、既存の機能の一般化でした。
>さらに、クロージャは、ネストして外側のクロージャからブレークしたり、包含関数から戻ったりすることができないため、ラベル付きブレークほど強力ではありません。
>いずれにせよ、この機能は安定しており、削除されることはありません。 >>571
これ書いててアンチパターンだと感じないの? 機能的な差異(できることの違い)を示すためのサンプルコードにアンチパターンがどうのとか一生言ってる奴はこの世のすべてのコードから大域脱出を排除してきて >>578
正しいアルゴリズムをぜひ知りたい
コードを出して 大域脱出自体がアンチパターンではあるが
それでも必要になったときのために比較的マシな方法があったほうがよいというのは両立する話なんだよな。 短絡評価の&&と||がResultやOptionで使えるようになればtryブロックはともかくlabeled breakは使われなくなるよ 短絡でブロック式がすべて死滅するとは思えないからラベル指定breakも死滅しないだろうな
ブロック式が途中で抜けられるようになっただけなんだし、ブロック式書いてて途中で式の値を確定したくなったらラベル指定breakの出番 >>582
それらを使えると>>571の例はどんな風に書き換えられるの? 50行や100行を超えるような関数を書いても何も感じない流派の人と
10行以下でもリファクタリングの要否を検討するような流派の人とでは
審美眼というか根本的な価値観が違うから
お互いスタンスの違いを認識しないと話が噛み合わないよ 数ヵ月前にstabledに入ってる機能とRFCすらマージされてないプロポーザルを比較するのって
捕らぬ狸の皮算用って奴じゃないですかね >>581
例えば>>571って大域脱出なのか?
無駄なループを回らないために早期離脱しているだけに見える
それがアンチパターンだと主張するならばどうやって無駄なループを回避するんだ? >>580
let ij = (0..100)
.cartesian_product(-1..100)
.find(|&(i, j)| if j == -1 { f(i) } else { g(i, j) });
match ij {
Some((i, -1)) => ff(i),
Some((i, j)) => gg(i, j),
_ => 0,
}
結局やってることはfindして結果に関数適用してるだけだよね >>588
それ動くけど
イテレータをi依存にされたり
判定方法を変えられたらすぐに破綻しちゃうな
さらに可読性がない上に保守性もない
そのコードは失格 他の書き方があるって主張は不要論になってないんだよな
ラベルbreakってシンタックスシュガーでしかないから
もともと実現方法があるのは当然
ある処理においてよりシンプルで学習コストが低い記法があれば
その処理では不要と言える f, g, ff, gg の要求する型がu8で上限値が128だったら >>590
判定方法変わらなくても>>592で終了
自然数しか出てこないコードに突然勝手に負数を導入し出すあたりJS畑から来たのかな >>587
・ パターンとして分類するとアンチパターンの範疇に入る
・ しかし必要な場面もある (からマシな方法としてラベル付きブレークが導入された) 適材適所を無視して何でもイテレータメソッドチェーンにしようとする人たちはたまにいるけど
多重ループで条件により早期で抜けたり何段かまとめて抜けたりする時は>>571のように素直に多重forまたはloopが分かりやすい
loop式だとそれ自体もbreakで値を返せるようになるがどの段のloopを抜けるかでラベル指定breakは今までも使われてきた
だからラベル付きブロック式にも違和感ないが拒否反応を示してる人は技術的ではなく感情的になっているだけではないかと思われる >>594
多重ループを早期で抜けるのがアンチパターンとは聞いたことがない
最近の多くの言語が持っているラベル付きbreakがC/C++は持っていないため多段抜けが出来なくてやむを得ずに状態変数管理することはあるが
それはC/C++言語の制約であり言語仕様が弱いだけなのでそこでの習慣に引っ張られてはいけない 論点1(578):>>571はアンチパターンなのか?
論点2(587):>>571って大域脱出なのか?
論点3(589):>>588 vs >>571 どっちがマシか? そんなに拘るのにID変わりやすい環境ならコテつけて >>585
どちらかというと
What中心でコードを構成する人と
How中心でコードを構成する人の考え方の違いじゃないか? >>596
私が >>581 で述べている「両立する」ってところを無視しないでくれ。
(形式としては) 多重ループからの脱出が大域脱出でありアンチパターンであることと
ラベル付きブレークが (回りくどいフラグ管理や goto より) マシな解決方法であるというのは両立すると言ってるんだ。 >>600
代替策を示さずにアンチパターンだと唱えるのをそろそろ辞めにしよう
多重ループから脱出しないコードを示してくれ >>601
「形式として」と念押ししてるんだから実態はそうではないというニュアンスを読み取ってくれ。 そもそも大域脱出とやらの定義はどこに示されてるの?
https://www.gnu.org/software/libc/manual/html_node/Non_002dLocal-Exits.html
> 23 Non-Local Exits
> Sometimes when your program detects an unusual situation inside a deeply nested set of function calls,
> you would like to be able to immediately return to an outer level of control.
> This section describes how you can do such non-local exits using the setjmp and longjmp functions.
GNUの文書では単にネストした関数から一気に脱出することだけど?
このスレの文脈としては何か別の特別な定義を参照してるの? 言い換えると、
形式としてアンチパターンであるが故にアンチパターンと同等の書き方を強いられていたことに
対する解決 (のひとつ) がラベル付きブレークだろうってこと。 >>602
多重ループからの脱出を形式として優れていると主張するならばわかるが
良き代替策もないのにアンチパターンだと主張するのは意味不明でありそんな主張はほかでも聞いたことがない アンチパターンなのに解決方法とか自分で言ってて違和感感じないの?
それともアンチパターンだけど必要悪とでも言うのかな? アンチパターンとなりうる大域脱出とは、関数呼び出しの奥深く、すなわちスタックレベルの異なる位置からの脱出を指す
一方で、ループやブロックのbreakは同じスタックレベルの出来事であり、当然ながらアンチパターンには該当しない アンチパターンの定義も大域脱出の定義もどうでもいいけど
-1を特別なダミー値として-1だったら条件変えてfindとかいうゴミカスクソコードよりは多重ループとbreakの方が遥かにマシなのは間違いない >>592
>>608
気になるならOption<u8>に変えればいいし、それで大した差なんてないよ
ちょっとRangeで楽できなくなるだけ
それくらいすぐ分かってくれ そういうやりとりしなきゃいかん時点でもうラベルブレークのほうがマシやん >>606
「パターンとして分類すると」「形式としては」アンチパターンに当てはまるだろう (実態はそうではない) と言ってる。
要するにアンチパターンではないと言ってるつもりなんだけど。 >>609
どんどんコードがガチャガチャ読みにくくなっていくので素直に多重ループとbreakしますね >>611
自分が
>>581 > 大域脱出自体がアンチパターンではあるが
と書いたことも忘れたのか?
そもそも形式的とかオレオレ定義の用語で語られても困るし デバッガにかけることを考えたらたかが十数行に収まる二重ループを直積という抽象化で隠蔽するより二重ループのまま見せた方がいいよね
直積として宣言的に書こうが結局プログラムは手続きとして実行されるので、過度な抽象化は手続きに展開する思考コストがかかるだけ > 大域脱出自体がアンチパターン
制御フローの代わりに例外使っちゃなんね!
ばっちゃが言ってた!
しかし彼女は大域脱出すべてについてではなく
その用法について言及したにすぎないのであった 「アンチパターン」に反応して急にレス増えすぎ
>>571がアンチパターンなのは多重ループから大域脱出してるからじゃない
多重ループを使った探索と探索で見つからなかった場合の処理を分離できてないからアンチパターンなんだよ
ブロックのラベル付きbreakを無理矢理使おうとした創作コードだからそうなる >>616
forの中から帰るときは見つかった場合の処理で、
見つからずにfor文抜けたらデフォルト値を返すなんて処理プロダクトレベルで1億万回使われてるけど >>571 の分岐構造
f(i) がtrueならff(i)
g(i, j)がtrueならgg(i)
それ以外は0
>>588 の分岐構造
jが-1ならf(i)がtrueなら Some(i, -1)
jが-1以外でg(i, j)がtrueなら Some(i, j) ただし j != -1
jが-1以外でg(i, j)がfalseなら None
↓
Some で j が-1 なら ff(i)
Some で j が -1 以外なら gg(i)
None なら 0
(^^;; 論点578:>>571はアンチパターンなのか?
論点587:571って大域脱出なのか?
論点589:>>588 vs 571 どっちがマシか?
論点607:ループやブロックのbreakは大域脱出ではないし当然アンチパターンでもない
論点614:588 は過度な抽象化は手続きに展開する思考コストがかかるだけ
論点618:588 の分岐構造の煩雑さ(^^;; >>616
代替策を示さずにアンチパターンだと主張するのをそろそろ辞めにしようぜ
まずは代わりのコードを示すべきだ let result = collection.find_foo().unwrap_or(bar);
find_fooの中で多重ループ使おうがイテレータ使おうがそれは状況に合わせて使い分ければいい
大事なのは適切な単位で処理に意味ある名前をつけて”意図”を伝えるコードを書くこと
特に多重ループで大域脱出させるような処理に名前も付けずダラダラ書くスタイルはRustのような言語では基本的にやっちゃダメ オジがわかってなさそうだから補足しておくと
見つかった場合と見つからなかった場合を明確に分けられてないのは>>571も>>588も同じだからね >>616
機能分離の粒度や方法は決まった正確が一つあるものではなく、状況に応じて基準も変わるし、複数の解があり選べることも多い
したがって、処理を分離したコードの方が明らかに望ましいと誰もが思う場合でないと、アンチパターンと言い切るのは過度だと言える
今回の>>571を、探索(=インデックスを得る)機能部分のみに分離できるのはご指摘の通りだが、コードはこうなる
let (i, j) = 'i_j: {
for i in 0..100 {
if f(i) {
break 'i_j (Some(i), None)
}
for j in 0.100 {
if g(i, j) {
break 'i_j (Some(i), Some(j))
}
}
}
(None, None)
};
元のコードと比べると、構造も行数も変わっておらず、さらに加えてこの後にmatch (i, j)の処理コードが必要となる
したがって、今回のケースで機能分離するかどうかは好みの範囲であり、元のコードに問題はないと言える >>621
>処理に意味ある名前をつけて”意図”を伝えるコードを書くこと
つまりラベル付きブロックでいいよね
現代的なエディタならブロックは折り畳めばいいし、ラベルで名前もつけられるし
何より型推論と?演算子に任せれば済むような非本質的なエラー型の合成というノイズをメインロジックから分離できて"意図"が伝わりやすくなるので >>572の比較表に付け加えるとこんな感じか
関数化:
〇機能名の明示 (関数名)
×利用する変数(値)と結果の型推論
〇変数、ライフタイムのスコープ
×環境(変数)のキャプチャ
〇早期脱出
×?演算子のエスカレーション・合成
ラベル付きブロック式:
〇機能名の明示 (ラベル名)
〇利用する変数(値)と結果の型推論
〇変数、ライフタイムのスコープ
〇環境(変数)のキャプチャ
〇早期脱出
〇?演算子のエスカレーション・合成 関数の特徴としては、
○コード記述箇所と実行箇所の分離
○再入可能性
というのがあるな。
まぁ、ブロックは記述箇所と密接に関係しているから当然の話だけど。 ブロック式はその場でしか呼ばれないから再入可能性はそもそも必要ないね
そしてブロック式がある関数として再入可能性を保証でいいわけだから >>623
そういうループを使う現実的なユースケースは
通常の多次元データではなくフォルダとファイルのようなCompositeデータを扱うときだけなので
インナーループから返す値にはiを必要とせずSome(j)で十分になるように作る
もしそれができない場合でも(Some(i), None)はSome(i)に(Some(i), Some(j)はSome(f(i,j))に変換できるようにする >>628
勝手に様々な特殊なケースのみに限定して話をしても全く意味がない
さらに二次元インデックスi,jをjだけに折り畳めるという特殊な仮定を持ち出すのも意味がない
そしてインデックスを返す機能の話をしているのにf(i,j)を持ち出すのも意味不明すぎる >>624
let message = 'get_latest_unread_message {
…
break 'get_latest_unread_message Some(Foo::foo_bar(result)
…
};
とか書くわけ? >>629
具体的なユースケース出せないなら話にならないよ >>631
いくらでも様々なデータ処理があるだろ
狭い範囲の仕事しかしていないから
>>628のような何重もの特殊な仮定だらけになるのだろう
そして我々がしている議題はRustの言語機能の話がメインでそこでの例として汎用的なサンプルコードが出ている そもそもラベルいらんよね?(構文上必要なんだろうけど?)
ブロックに区別が必要ない場合は
>>630を例に取ると
let latest_unread_message = 'b { // 変数名がブロックが返す値を表現
break 'b Some(Foo::foo_bar(result)) // 具体例はbreak以降に表現
};
ブロックが二重三重になってるときだけそれを区別したいだけであって? 型推論のおかげで>>626の機能を諦めれば記述量を大幅に減らせるってことなのかな?
>>630
これに、breakで一番内側の名前付きブロック名を省略する記法があれば、こんな感じに出来て、エディターで表示補完も出来るな
let message = 'get_latest_unread_message {
…
break '' Some(Foo::foo_bar(result)
…
}; >>634
ブロックは関数と異なりリエントラントの必要性も概念もない
ブロックは関数内のその場所で呼ばれるだけだから他から不意打ちで呼ばれる対策も必要ない
ブロックが所属する関数がリエントラントを保証しているから大丈夫
そしてブロックのメリットは環境変数をそのままキャプチャできて型宣言は必要なくブロック式の結果も型推論されること
さらに?オペレータがifやmatchやループの時と同様にブロックを飛び越えてくれるからResultを気にせずに値だけの型に専念できる >>633
なるほどね
いっそラベルをretとかにしてしまえばクロージャっぽく見えるのか
let latest_unread_message = 'ret {
break 'ret Some(Foo::foo_bar(result))
}; >>633
ラベルは必要
ラベルが付いてるブロック内でのみラベル指定breakができる
例えばforの中でmatchでアーム右辺が無名ブロックで無名breakしている既存のコードはそのブロックをbreakせずforをbreakする
しかしそれら全体がラベル付ブロックに覆われていてラベル付breakしていればラベル付ブロックをbreakできる
既存コードと互換性があり曖昧さもなく良い構文となっている
既存のloop構文からloopキーワードを抜いただけの構文である点からも自然な受け入れやすい構文拡張となっている 同時に導入されたlet elseも便利やな
それまではif letするたびにネストがどんどん深くなっていって困るかそれを回避するために
let xxx = if let Some(xxx) = foo() { xxx } else ...と三度も変数名を書かされて悲惨だった どちらも長年Rustで構文的に残っていた不便な点だったから解決してうれしい >>639
早速テストコードに使ったら、ifを除去出来て大変満足 >>642
え?!
elseの後の型は!じゃないとlet else使えないよ >>642
例えばこれは深さ3段だからギリだけど
どんどん深くなるのを早期continueしたい時にどうする?って話
環境問題などで別関数化も無しでね
for x in iter {
if let Some(boo) = get_boo(x) {
if let Some(foo) = get_foo(x) {
if let Some(woo) =get_woo(x) {
let pigs = three(boo, foo, woo);
...
}
}
}
} >>644
関数化無しの前提を置かないといけない状況を改善した方がいいのでは?
その例も普通ならxを受け取ってOptionを返す関数に簡単に抽出できるよね? >>645
それは無理
実際のコードだと全部揃ってから処理ではなく途中で処理しつつになる
さらにbreakによるfor抜けも入ったりする
さらに使う環境が多くてその別関数へ参照渡しまくりになる
さらに途中でエラーも返るから別関数はOptionではなくResult返しになる
つまり?はOptionに対して直接使えない 関数を分けると?を逃がすことが出来なくなる点など含めて
ラベル付ブロック式の時と同じ話だな
いずれにせよ関数化は愚策でありlet elseによりcontinueするのが正解 continueはforでnext()に進んで無限ループじゃないぞ ちゃんとしたプログラマーは無限ループになりそうなところはブレークMAXカウンターやシグナルブレイク出来るように書いてるから大丈夫ですYO! サーバーやデーモンなどは同じことを繰り返せるように敢えて無限ループにプログラミングする >>646
なるほどResult<Option<T>>でOptionだけハンドリングしたいパターンならif let使えばネストしていくわな
普通にmatch使えばいい場面だと思うんだが何かlet else使うメリットがあるの? >>653
matchとif letは同じ
コード書けば分かる ああif letの結果を受けるように書くからってことね
それ普通は使わないよね let foo = match get_foo(x)? { Some(x) => x, None => continue} が
let Some(foo) = get_foo(x)? else { continue };になると行数が短くなってうれしいということ? matchを使おうが元の問題これ何も解決しなくね?
>> let xxx = if let Some(xxx) = foo() { xxx } else ...と三度も変数名を書かされて悲惨だった Someの中の文字は何でもいいんだから短いスコープなら同じ名前を律儀に書く必要ない 変数名短くしても結局3度も書かされるのは異常で改善の余地あり
それが1度で済むようになってようやく正常化した swiftのようにguard let ~ elseとしてくれれば
行頭見るだけでguardだとわかって良かったのにな
少し複雑なパターンマッチだとelseが埋もれてしまう 冒頭だけで分かるよ
let x = ... ←通常
let Some(x) = ... ←elseあり >>662
両方の観点から分かりやすい
普通のletではなく、ifにより条件付きだと分かる
普通のifではなく、letにより新たな変数定義付きだと分かる >>661
let Foo { bar, baz, … } = get_woo?.map_or_else(Bar::default(), |x| … >>664
その観点からはRustではrefutableなのかirrefutableなのかが一番重要な概念になる
噛み砕いて言うと条件付マッチングとなるのがrefutableで無条件マッチングとなるのがirrefutable
Foo { bar, baz, … }はirrefutableパターンだから普通のlet
Some(foo)はrefutableパターンだからif letかlet else >>665
おおいいね
じゃこれは?
let foo::Foo { bar, baz, … } = get_woo?.map_or_else(Bar::default(), |x| … >>666
letについては既に説明した通り
それはFooとBarは型が異なりエラー
型の一致は必須
struct内の略は...と3つでなく..と2つ
Bar::default()の()は付けてはいけない
型の明示が必要ないならDefault::defaultでもよい
map_or_else(Default::default, |x| …)は
map(|x| …).unwrap_or_default()と書ける >>662
elseがある場合はmatchのほうが可読性いい
elseがない単純な場合はif letでも悪くない
ネストすると最悪 letするたびにインデントが深くなる言語もあるぞ
そういう失敗を記憶しているおかげで、比較的成功した例を認識できる let elseはシンプルなケースじゃないと通常のletと見分けがつきにくい
でもシンプルなケースならlet chainでまとめた方が圧倒的に可読性高まるのでいらない子になりそう 最初見たとき便利そうだな思ったけど意外と使わない let else
? とか unwrap_or_elseとかで事足りることが多い
あって困るような機能でもないけど 論駁可能性の有無で区別つくし、elseによる処理文があるのだから、let elseが見分け付かないなんてことはない
if letと同様にlet elseはエラー値を捕獲する構文ではないため、エラー処理に用いないのは当たり前で、?を持ち出す人はその基本を理解できていない
マッチングしない時にcontinueやbreakする時などは、特にlet elseが最も適している やってることは同じ早期離脱なのに
新しいバインディングを伴う場合は
breakやcontinueならlet else
returnなら主に?かlet~match
新しいバインディングを伴わない場合はifかmatch
書くのは多少楽になってもこれだけ種類があると読みにくくなる if letを廃止してmatchだけに統合すればすっきりする
match boolでifも廃止できる
breakとcontinueもmatch boolで括れば済むから廃止できる >>677
それだと'outer指定ができなくないか? C/C++は多重forの多重breakを持たないが状態変数などで工夫して対応できている >>679
>工夫して対応できている
これ言ったら自分の発言のブーメランになってないか?
そりゃ言語仕様に盛り込まずに工夫して対応しろってことなら、
なんだって工夫しろで終了じゃん いや、工夫する方が正解という信念とかは持ってなさそう
正解なんてあるわけないという結論から逆算されたムーブじゃないか >>677
>>659にある3回書く問題を無視していいなら今でもmatch縛りで書けばいいよね
その方が読みやすくなる面があるのは確か >>680
gotoの導入いいね
loopとforも廃止してシンプルにmatchとgotoだけで見やすそう デストラクタがあればgotoの少なくとも一部は許されないことが確定する
なければ雰囲気で決める >>675
たまたま自分で使うことが稀だからといってcontinueやbreakを要らない子扱い? そりゃアルゴリズムは出来合いのものしか使いませんっていうならcontinueもbreakも使わんだろうな。 なにさ!ループの中へジャンプしたっていいじゃない! 昔みたいに関数を長々と書く時代じゃないから
breakやcontinueは関数抽出で不要になることが多くて出番がすごく少ないんだよ 100行関数の時代の反動で5行の処理でも関数切り出しする時代からのさらに揺り戻しで
最近はむしろ型推論に任せて明示的に型を書かないために全体が一望できる程度の30行ぐらいの関数は増えているので
breakもcontinueも普通によく見かける ?はunwrap or return (Err/None)
.unwrapはunwrap or panic
欲しかったのはこれらと同じように使える
unwrap or breakとunwrap or continue
でも出来たのは新しい構文のlet elseでしたとさ https://rust-lang.github.io/rfcs/3137-let-else.html#examples
RFCのExamplesなんてbeforeもafterもlet else以前の問題
typestate使ってtry_fromとnewを用意するというrustのidiomを使えば良いだけなのに
実施して当然のリファクタリングもやってないようなコードを前提に議論が進んでるのを見ると悲しいね >>692
それはRFCつまり単なる叩き台
無意味に長いサンプルだからThe Rust Referenceでは短く書き換わっている
typestateは適用できないケース、不向きなケース、向いているがそこまでやらなくてもいいと判断するケースがあり
それ以前にlet elseとは無関係だから持ち出して批判するのは揚げ足取りにしか見えない >>686
稀と言ってるだけ
確かに俺はアルゴリズム博士じゃないね 特定のサンプルに対してこう書けるから要らないと主張したり、
自分は使わないから要らないと主張したり、
それらの主張は何の意味もないことにそろそろ気付かないと 関数の「切り出し」とか行数で語ってるのとかまだまだ幼いなってオモタw
最近の流れじゃあ>>689サンだけ一個上のレベルにおるな >>685
> デストラクタがあればgotoの少なくとも一部は許されないことが確定する
ならtry〜throw〜catchで >>696
君はまだ参照の持ち回りや明示的に型を書くことのデメリットを理解せずになんでもかんでも関数にするレベルにいるんだね >>693
最終的なRFCはたたき台じゃないよ
RFCは議論の進行とともに更新された上で提案された機能を採用するどうかを判断するための公式ドキュメントとなる
採用決定後に書かれるリファレンスとは全く役割が違う >>689
関数切り出しでは出来ないこともあれば無駄な記述が増えることを含
めて様々なデメリットもあるから、
それらをクリアできて上回るメリットがある時に絞って使い分けるとよい
あと関数切り出しすれば何でもcontinueやbreakという制御構造を無くせるわけでもない
そして君の使い方だと出番がないからそれらが不要だとの主張には何の意味もない >>700
だから議論中はたたき台だろ
会社の提案資料とか書いたことないのか? >>688
フフッ、奴らはRust魔術師なんて名乗ってるが所詮は、正確な儀式魔法陣の設計と、正確で美しい呪文の詠唱にこだわる愚物よ!
それで発動するのが初級のファイヤーボールたるファイル検索とか笑えるだろ?
無詠唱でループで取り囲まれている敵陣真っただ中に転移ジャンプすることすら叶わぬのだッ! >>702
ユーザーマニュアルと開発時の要件定義書や設計書の違いだよ
承認された最終版の要件定義書や設計書をたたき台と呼んでる会社なのかな?
提案資料と捉えても別に構わないとは思うが
採用判断の妥当性を確かめるために参照するのは間違ってもユーザーマニュアルではない 実装は正確に、商談はいい加減にやればいいのに
消費税込み0円で物々交換することすら叶わぬのだ >>673
Option,Resultならunwrapで良いけど、任意のenumから同じように中身を取り出す方法ある? if letとlet elseがmatchのsyntax sugarなのは自明だから
RFCのサンプルという特定の例に対して別の書き換えが可能だと主張しても何の意味もない
if/let/elseという既存の概念と既存のキーワードの組み合わせによりmatchより短く書けて利便性も良いと総合的に判断されてRustに導入された >>704
> 承認された最終版の要件定義書や設計書をたたき台と呼んでる会社なのかな?
お前さんが
>> だから議論中は
という簡単な文章すら理解できないことはよくわかった RFCは叩き台だと思う
たたき‐だい【×叩き台】
読み方:たたきだい
批判・検討などを加えて、よりよい案を得るための原案。 >>708
じゃlet elseのRFCは議論中じゃないからたたき台じゃない
>>693の認識が間違い RFCに議論の過程もなぜ導入しようとしたのかもメリットも書いてるのに
サンプルコードが別の書き方できるってもう重箱隅つつきたいだけでしょ
せめてRFCテキスト一文に対する反論なり書いてれば議論になるんだろうけど let elseの話ならばもはやRFCの段階ではなく、
既に正式にlet文の一部として位置付けられて取り込まれており、
公式Referenceにもletの機能として必要な条件などが記載されている段階 RFC (request for comment)はもちろんその名の通り『叩き台』で合ってる
ただしlet elseについては既に結論が出ているので、
RFCのtracking issueと合わせて過去の経緯と議論を見るための存在へと変わっている
今はRust公式Referenceを見るだけで十分 複オジさんの役に立たない自演珍説言い訳レスはもうお腹いっぱい 3ヶ月も前にstableに導入されたlet elseやラベル付ブロックbreakを今になって自分は使わないからその機能は不要だと主張している人は何をしたいのだろう?
時期的にも不明であるし執拗に不要だと言い続けている意図も不明だ
同時に導入されたGATも使わないから不要と言い出しそうだな >>717
ようわからんけどお前の発言の意図も不明だぞw そもそも要らん機能だと言い張るならRustフォークして削除した版作って勝手にやってりゃいいと思う 有意義ではないとは言ってない
使うのは稀だと言ってるだけ
稀で済むからRustを使ってるの Cの時代にはgotoは普通に要る機能だったからな
コンパイラも今ほど賢くないし gotoなんてベーシックしか使ってことない初心者しか使ってなかっただろ gotoがメモリ消費量や実行性能の関係で最適になるケースがあった
コンパイラの最適化も今ほど優秀でなかった C/C++でもラベル付break/continueをサポートすればgotoを廃止できるけど
過去のgoto文があるために手を付けれないだけでなくwarning化することもなく
ラベル指定break/continueも導入せずにgotoのままでいいやと怠慢かなあ
どうしてもgoto避けたいなら無駄にフラグ変数利用 let elseはErrにアクセスできたなら
Option/Result系の早期離脱はlet elseに統一できてよかったんだけどね
それができないから?オペレータが使えないbreak/continueか
Errにアクセスする必要のないOption::ok_or_else + ?の代替 >>706
任意のenumから中身を取り出すのは抽象化すれば結局はOptionになる >>727
if letとlet elseは対であり対称な存在だから
「Errにアクセスできたら」なんて意味不明なことを言い出してる時点でif letも理解できていない
まずはif letが適していることとできないことを勉強し直しなさい >>729
それはものすごく一面的な見方
内部実装だけみてユースケースを見てない >>728
中身を取り出す方法を具体的に尋ねられているのに「抽象化すれば」では答えになっていない >>731
既存コードをlet elseへと適しているのを置き換えたから把握している
Err(e)値も処理したいときに
if let Ok(v) = ...と書かないのと同様に
let Ok(v) = ... elseも同じく使わないのは当たり前
それはどちらもmatchやResultメソッドを使うべきケース
もしf()がResult<enum, Error>を返す場合ならば
if let Xxx(v) = f()? ...とErrだけ逃がすことができるし
let Xxx(v) = f()? ... elseとErrだけ逃がすことができる点も同じ >>731
糖衣構文の実装ありきでユースケースベースの検討を疎かにしたRFCと同じだねぇ RFCの段階での議論に敗北して採用されちゃったからって5chでギャオってるの見苦しすぎるだろ 機能追加は自分の思いつくことを解決する、または大幅な改善をする
銀の弾丸でなければならないって思ってそうだ 条件つき確率を信じない人のような印象
条件が成立しなければ普通の弾丸だから 「と言い出しそうだな」「って思ってそうだ」「な印象」
関心事がけっきょくは発言者の人格なんでつねえw 5chの隅っこでとにかくマウント取りたいだけの人だから ただ文句をつけたいだけなのか
とにかく嫌いなだけなのか
ブロックラベル指定breakについてもlet elseについても反対してる人から一貫性や理性を感じられない面はある
例えばlet elseについて不要との主張の理由がこれまで3つ挙げられているが
「自分は使わないから不要」は論外として
「他の書き方ができるから不要」も特定の例にしか適用できない代替策だったり当然if letとlet elseはmatchに代替できる原点の視点を欠いている
「Err(e)値を扱えないから不便で不要」も同様でif letとlet elseはそれを扱わない構文だと理解できていない
もしif letも不要だと開き直るならばそれはそれで主張が一貫しているのであろうが ぱっと見の印象ほど便利でもないな
と言われてるだけのような気がするが 理性と一貫性が一番欠落してるオジが何か言ってるぞwww 冷静な技術的議論にならない原因が誰にあるかは一目瞭然なんだから相手にしないこと
分かりやすい煽りよりも技術的議論に見せかけたマウンティング行為にこそ高いスルー力が求められる 煽りこそ要らない
技術的な議論なら歓迎
マウンティングとか興味ないためそんな発想も感覚も出て来ないので感じている人はたぶんそれ鏡の自分自身 技術的論議とか不要やん
売れるシステム作ったもんの勝ちや
でもって世の中で売れてるシステムって意外とクソ技術 >>746はきっとこの業界長いなw
昼間書き込んでる学生や無職どもとは面構えが違う >>746
ここはプログラミング言語Rustのスレ
興味がないならば別スレへどうぞ
>>747
昼間に書き込んでるIDを見たらあんたやでw >>748
勘違いしちゃいけない
言語には興味があるが今行われてる議論には興味も無いし意味もない 何年も前からRFC出てて昨年stabled入りした機能に要るも要らねえもねえからな stableになった11月に不要だと暴れるならともかく
今になって不要だと暴れてるのも不思議だよな 🦀
みんながインターネットで争ってばかりいるので、Ferrisくんは踊るのをやめてしまいました
お前のせいです
あ~あ RFCに特攻しないだけマシなのでは
いや公式に真正面からお伺いたてたほうがマシなのか 暴れてる、ってのが被害妄想的表現やわ
俺には誰一人暴れてるようには見えない
このスレで暴れてたのは所有権の複製のあの人だけ 繰り返し同じ主張をしても誰も同意してくれないから
自分の意見に同意してくれないやつらは全員暴れてるということにして無意識に自我を守ろうとしてるんだよ
自己愛的防衛とか躁的防衛というやつ >>754
所有権の複製ってもう5年くらい前だろ
そんな昔のことをいまだに引きずってるとは病んでね?
何の記事に書いてあった話かも忘れた 頭が良くなれば解決することを、性格や健康の問題だと思わないほうがいい気がする
頭が良くなりたいのは色々解決するからだ
マウントしたいからではない 俺たちが興味あるのは技術の話題であって他人じゃねーもんな
誰がどうしたとか妄想に取り憑かれているとしか思えん
ところで標準ライブラリがflumeではなくcrossbeamを取り込んだのはなんでだ? https://github.com/rust-lang/rust/pull/93563
> crossbeam-channel has become the most popular alternative on crates.io
これ以上の深い理由はなさそう cargo check --doc-tests
欲しい! std::sync::mpscの実装が5年くらい放置されてた
その間に効率的な実装のcrossbeamが広まり更に効率の良いflumeも登場して性能と人気を競っている
放置されてた標準ライブラリを今回は実績のあるcrossbeam実装ベースへ変更したが状況は変わらないだろう
Rustは最小限の標準ライブラリに様々なクレートが競い合ってより良いものを生み出していく文化であるからだ 自分の発言が炎上するかしないかを自分で判定できそう
失言おじさんにはそれができない ずっとおじさんおじさん言ってる「おじさん」おじさんの方が正直こわい オジおじ言ってる人は相手にしちゃいけないアンタッチャブル 「所有権の複製」とかいうアホな用語を使って
ここのみんなにバカにされて涙目で敗走していったおじさんがおるんよ
その可愛そうな子が複オジ >>768
誰かが「所有権の複製」と言い出したのではなくITメディアサイトのRust入門記事に書かれていた話だな
その表現を許容できる範囲か違和感あるかで色んな意見があったのは覚えている Copy実装型は値が複製されて新たな所有権が生じるが正確
それをその入門記事は所有権の複製と表現してたやつだろ
許容でも不許容でもどうでもええわ そんな昔の話をいまだに引っ張ってオジオジ言ってたのかよ
精神病んでるな C言語は知ってる。アセンブラも知ってる。どうやってメモリ安全なのか
サクッと説明できないの? 文句あるならRFCとか記事サイトとかに直接言うしかないな
ここでこれ以上の議論を続けてもしょうがない 複製の方はググってみたらitmediaのRust入門記事だね
型に依って移動と複製の2種類あるのを教えるところだから、入門者向けの解像度として大雑把にしといて後で詳細を学べばいいでしょ
メモリ安全の方は解説記事が山ほどあるから、まずは読んで分からないところだけを質問したら? 参照されている間はmove禁止にする
それができたらその意味で安全
どうやってそれができるのか実装依存かな 稀に誤解する人がいるけど、moveはメモリ上の移動ではなく所有権の話だから注意ね
メモリ上の移動の禁止を保証するのはstd::pin::Pinだけど、初心者は知らなくても大丈夫 moveを所有権の移動だと捉えてるから
copyも所有権の複製だと勘違いする
これが世に言う複製おじさん >>783
一応ちゃんとCopyのdocで「It’s important to note that in these two examples, the only difference is whether you are allowed to access x after the assignment. Under the hood, both a copy and a move can result in bits being copied in memory, although this is sometimes optimized away.」って説明されてんだよな 所有権が移動するからその後はアクセスできなくなるんだよな >>784
Copy非実装型は所有権が移動するで合ってるぞ
それをmoveと呼ぶ メモリ安全に関しては実行時の、
すなわち実装上の工夫があるわけでは一切なく、
単にメモリの使途をコンパイラが把握できるようにして
コンパイラが厳しいチェックを入れるってだけのことなのかな
極めて有用だと思うけどつまんないね >>789
実装上の工夫をコンパイラが自動的に出来るように、メモリの使途をコンパイラが把握できるようにしているんじゃないの? >>791
出来上がるプログラムのメモリ実装はC/C++並みに速く、
なのにそれはコンパイラの厳しいチェックをくぐってて安全
それだけのことでしょ 他言語由来なんだろうけど、moveという命名はセンス無いわな。
transferあたりならまだマシな気がする。 moveがふさわしい
ドキュメントでも至るところでmove or copyと対比され使われている
transfer or copyではおかしい moveやcopyの対象はvalue
ownershipはtransfer(譲渡/移転)されたりtake(取得)するもの
moveとcopyが対比されるのは同じ対象について述べているものだから当たり前 >>795
それな
そのことをずーっと理解できないやつがおって草 同じことを言い合っていて、間違ったことを言ってる人もいなさそうだが、何をもめているのだろう >>795
それらのふるまいの違いを明確にしないとただの言葉遊びに見える >>793だけ理解できていない
Rustで使われているmoveという用語は所有権が移動することで値が移動する概念を指す
例えばエラーメッセージのmove occursなどのmoveはその事象を指している
だからそこはtransferではなくmoveで正しい RustはC++から輸入して所有権って言葉を使っているだけであって
C++で言う「所有権を失ったがオブジェクトとしては存在している」みたいな状態は少なくともsafe Rustには無いからね
だから値と所有権を切り離して考えるだけ無駄 値と所有権を切り離して考えてもムダなのはその通りだね
値には必ず所有者(owner)すなわち変数が対応してる
そして所有者である変数がスコープを尽きて消えるときに値もdropで消える
だから切り離すことはできない
値が消えないためには所有者である変数のスコープが尽きる前に、他の変数へ代入か、関数引数渡しか、関数戻値返し、をしなければならない
その時にCopy実装型だと値が複製されて渡した先にも別の所有者が生じて各々が別の人生を辿る
!Copy実装型だと値が「概念的に」移動して渡した先に所有者が変わる
この両者の違いを端的に表した用語がcopyとmove 所有権が「移る」という意味の英訳として「move」という単語を当てるのが間違ってるわけではないが
「所有権を複製する」といった致命的な間違いを生む原因になってるの可能なら避けた方が良い >>802
いや、所有権に対して直接moveと言っているわけではないんだよ
値の所有者(=変数)が変わることを、視点を変えると、旧所有者から新所有者へ値が移動している、それはさらに視点を変えると、所有権が移動していると表現してもいいわけだけど、
それら視点は様々あるけど同じことを指してRustでは『move』と呼んでいる
だから所有権(ownership)という単語に対して一般的な英語の用法として動詞にtransferやtakeを文章では使うけれど、そのこととは全く関係ない いや、そんな状態が無いはさすがに嘘か……
Option<T> where T: Dropのtake()は実質ムーコンみたいなことやるし
所有権=リソースの解放義務と考えればそもそも!Dropには所有権なんてものは存在しないって考え方が正しいのかねえ >>804
その場合も値と所有者は単体には生じてない
take()で新しい所有者に値が移動している
ただしデフォルト値のNoneにスワップされるため元の所有者も生きたままというだけ
これは一般的にはstd::mem::swap(),replace(),take()であり
互いに相手にmoveが生じただけ exeを実行するみたいなトートロジーは極めて有用だけどつまんないね やっと分かったかも
コードのある時点において、仮にその時点での継続を全部消したとき、Drop::dropが自動で差し挟まれることになるブロックはその時点でそのリソースを所有している、と解釈できるわけか
というのがNLL以前の話ってやつなんだなたぶん
そうなると所有の主体はブロックじゃなくてライフタイムってことになるのかね? どうしてドキュメントと違う言い回しの独自解釈を捻り出そうとしてしまうのか。 >>807
値の所有はそれを他へ渡さない限りレキシカルにブロックスコープが尽きるまで続く
ただし参照のライフタイムについてはレキシカルなスコープではなく実際の使われ方に応じて短くなりうる
例えば非可変参照を使っていて、後から可変参照を使うことになった時は、
それらが重なってなければ非可変参照のライフタイムは終わった扱いにして、同じブロックスコープの中で新たに可変参照を借用できるのがNLL 以前指摘されたDrop Obligationを今になって勉強して一人芝居してるw
ほんと滑稽な人やでww 説明をわかりやすくするために創作された抽象概念と実装が1対1で結びついてるわけないのにね >>800
>「所有権を失ったがオブジェクトとしては存在している」
値が所有権を持ってるわけじゃないから 値に所有権フラグ的なものがくっついてるイメージでとらえてしまってるんでしょ
だからコピーしたら所有権も複製されるし
値と所有権を切り離して考えても無駄だと勘違いしてしまう
全部つながってる >>816
値と所有者はそれぞれ単独に存在できないので、値と所有者を切り離して考えても無駄なのはRustの基本たぞ
値は必ず所有者を持つし、所有者は必ず値を持つ 複オジの常識は
Rustの非常識
このスレでは常識 >>807
っていうかそこまで難しい話じゃなくて
move a value と transfer its ownership を同義語として、自然言語としてわかりやすいように適宜言い分けてるだけなのか?
じゃあ結局「ownershipとは〜〜」という形でのownershipの定義なんてものはRustにはない?
公式のドキュメント検索しても見つからなかったんだよね >>817で合ってるよ
オジ使いの人は文句をつけることが主目的なのか正しいことにも文句をつける印象 不動産みたいな移動できないものの代わりにその所有権を移動する
メモリ上では実は複製と消去しかできないので本当は移動ができない >>821
そこの理解が重要だよね
あくまでもmoveは概念上のもの
だから皆が概念という単語を使っているね
>>799
> Rustで使われているmoveという用語は所有権が移動することで値が移動する概念を指す
>>801
> !Copy実装型だと値が「概念的に」移動して渡した先に所有者が変わる
> この両者の違いを端的に表した用語がcopyとmove そしてmoveの概念とは異なりmoveの実際の生成コードは最適化によって多種多様
基本はmove先に複製して元が無効となるけど、最適化によって複製が発生せずそのまま使ったり、
関数でサイズがある程度あるものをmoveで返す場合には、関数呼び出し元のスタック上に呼び出し先関数から直接書き込んでしまったりもする 書けば書くほど的外れになっていくのが凄い
やっぱり基礎が重要なんだね 言い掛かりつけるだけの人はこのスレ常駐の荒らしだから無視しとけ たまにはCloneのことも思い出してあげてください moveは値と所有権の移動
copyは値と所有権の複製
cloneは値と所有権の複製
borrowは値と所有権の借用
mut borrowは値と所有権の書き換え可能な借用
(๑•̀д•́๑)何ら問題ないキリッ
www 所有権の複製って聞いておかしいと思わない子もそりゃいるよ
みんなが賢いわけじゃなくて残念ながらアホな子もそりゃいるんだよ
あんまりいじめてやるなよ 弱者は強者に負けるとかいう理論もまた創作なのに
創作と現実が一致するまで戦争をやめられない現象は実際に起きている よほど誤解を招きやすい情報ソースがあるのか
それともここにはアホの子しかいないのか
はたまた一人が暴れているだけなのか C++のリファレンスカウンタの仕組みからやってないヤツにはちんぷんかんぷんだろうぜ 経緯は>>770の通りだろうけどそんなに引っ張る話なのかがわからんね。
それで実害があるならともかく。 病気な人が荒らすためのネタとしてやってるだけだろうからスルーが一番 >>830
>はたまた一人が暴れているだけなのか
100%これ
数ヶ月このスレにいれば自ずとわかってくる >>823
Rustは様々な点で実装(生成コード)と概念を明確に分けてるね
概念の部分だけを保証するから最適化の余地が大きくて良い感じ 概念にも最適化があって概念の「わかりやすさ」を最適化するために
moveとtransferどっちが優れた概念かを決めるバトルをやってたんだな >>837
Rustではその概念に対しては一貫してmoveを使っていてtransferが出てくるのは一般動詞として Interface 2023年5月号予告
Cと比較して理解する 質実剛健 Rust言語
3月25日発売 (定価 1,200円+税) 動かして学ぶ!Rust入門
発売日:2023年04月24日
定価:3,960円
【本書の概要】
Rustのプログラミング手法について、サンプルを元に手を動かしながら学ぶ書籍です。主に以下の3つについて丁寧に解説します。
●Rustの概要と開発環境
●Rustの基本的な文法と機能
●Rustによる簡単なアプリ開発
なお本書はエンジニアのための情報共有コミュニティ「Zenn」で公開されている大人気の「Rust 入門」を元にした書籍です。
【対象読者】
Rustに初めて触れるプログラマー
【本書のポイント】
・基本的な文法について丁寧に解説
・Rustの機能を学ぶのに適したサンプルを用意
・学習をもっと深めたい方に向けて「メモ」や「参照」で補足
【目次】
Chapter1 Rust
Chapter2 環境構築
Chapter3 Rustの文法と機能
Chapter4 アプリケーションの開発 >なお本書はエンジニアのための情報共有コミュニティ「Zenn」で公開されている大人気の「Rust 入門」を元にした書籍です。
あーはいはい
結構です https://zenn.dev/mebiusbox/books/22d4c1ed9b0003/viewer/30450c
束縛
> let 文を使うことでオブジェクトと変数を 束縛 します.変数はそのスコープから外れたときに束縛していた所有権を放棄します.
> また,最初に束縛したオブジェクトの所有権は基本的に原本となり,原本および仮の所有権がすべて放棄された時にオブジェクトは破棄されます.
原本?
仮の所有権?
( ^ω^)… 少し読んでみたけど笑いを通り越して背筋が凍るんだが
ほんとにこれの書籍化?
URL間違ってない? オレオレ用語満載の書籍が
初心者界隈に何をもたらしてしまうか…
発売前にぜひご一考願いたい( ^ω^) 仮に (あくまでも仮に!) 完全に挙動を説明できているのだとしても
他の人が書いた説明と合わせて読もうとしたら食い違うわけだろ。
書いている人はわかりやすく言い換えてるつもりなんだろうとは思うけど
学習過程全体を考えたらあんまりわかりやすくなってない。
Rust の全てをカバーしてて他の資料を読む必要がないくらいに充実してるなら
独特の定義で押し通すのもありかもしれんが、現実にはそんなことありえないしな……。 >>843
オブジェクトという単語は各プログラミング言語で様々な意味で用いられているが
Rustでその文脈で用いられることは少なく
その説明ではオブジェクトではなく値(value)とした方がよい
Rustで用いられるオブジェクトの用法の多くはtrait objectであり
Rustで重要なオブジェクト安全もtrait objectの話 >>848
> 他の人が書いた説明と合わせて読もうとしたら食い違うわけだろ。
> 書いている人はわかりやすく言い換えてるつもりなんだろうとは思うけど
> 学習過程全体を考えたらあんまりわかりやすくなってない。
そうそれ
無駄で邪魔でしかない
独自用語を野放図に用いる傲慢さ
用語の正しさに対する誠実さが無い パーフェクトRust買った
これまで日本人の書いたRust本の中で1番良い出来だな
変なところに偏ってないし 参照が入ってる変数は仮でもなんでもなく参照の所有権を持ってるのに仮の所有権とか言われても困る オブジェクトと所有権を切り離せないものと考えてる辺り>>800と全く同じ見解じゃん
ご本人様ではないですよね? たぶん参照自体じゃなく参照されてる値に対する「借用」のことを「仮の所有権」と表現してる
当然のように「オブジェクト」って言葉を使ってるけどもう少しvalueに寄せてほしいな
>> オブジェクト
>> 数値や関数や参照など,型の実体はすべて**オブジェクト**です.
>> つまり,式が返す値もまたオブジェクトになります.
>> 例えば, 1 という値も数値オブジェクトであり,1 == {1} という関係にあります.
ここの進次郎構文は何を表現してるの?
{1}が1なのはブロック式だからなんだけど >>800だけどちがうよ
所有権なんて存在しない見解だよ >>852
参照の所有権とは言わない
値に対する所有の唯一性が所有権で
値に対する参照が借用
そして所有>借用の生存期間ルールを満たした上で所有のスコープルールとなるため、
>>843
>> 原本および仮の所有権がすべて放棄された時にオブジェクトは破棄されます.
この文章の各用語を「仮の所有権」→「借用(参照)」など全て正しく書き換えても根本が間違っている
この説明だと参照が残っていれば所有者がいなくなっても破棄されないことになってしまうため >>855
>所有権なんて存在しない見解だよ
所有権なんて存在しないてどういうこと? 一個一個挙げてってもキリがないけど
> 所有権
> オブジェクトには 所有権 (Ownership) が付いています.この所有権には2つの属性があります.
付いています?
二つの属性がある?
( ^ω^)… 「参照」なんて呼んだって中身はただのポインタだから
アドレス値という値を持ってるし、当然その値の所有権もあるし、
&TはCopyで複製される値だし、&mut Tは非Copyでmoveされる値だぞ >>853
値と所有者/所有権を切り離して考えるのが無意味なのは正しい
Rustでは値には必ず所有者が存在し、所有者は必ず値を持ち、これが所有権
それぞれ単独では存在できないため、切り離して考えることは無駄で意味がない >>854
何かしらの型の実体という意味で
オブジェクトという言葉を使ってるみたいだよ
値をオブジェクトと言い換えるくらいは可愛いもの これ推理するの楽しいな
型の反対はオブジェクト
式の反対は値
そうしないと値の反対が二通り存在することになりかねない >>859
もちろん参照は実際にはポインタとして登場することが多いけど、必ずしもポインタが登場するわけではなくその保証はない
参照という概念レベルでのみ保証されており、最適化でポインタなんて消えることも多いため、ポインタの存在を仮定してはいけない
もちろん値としての参照がCopy実装型で、可変参照が!Copyなのはその通り >>857
自分が見つけられてないだけなら先に謝っておくけど
Rust公式ドキュメントも含めて誰も(値の)所有権という言葉を定義していないように見えるんだ
つまりそんなものは無いんだと今は自分の中で結論している
とか書くと狂人判定されるんだろなあ、もういいけど >>864
?
&T と&mut Tはポインタだよ
https://doc.rust-lang.org/reference/types/pointer.html
Pointer types
References (& and &mut) >>865
https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html
Ownership Rules
First, let’s take a look at the ownership rules. Keep these rules in mind as we work through the examples that illustrate them:
・Each value in Rust has an owner.
・There can only be one owner at a time.
・When the owner goes out of scope, the value will be dropped.
「所有権」はownershipの訳語だから、このルールにおける「ownerであること」がownershipの定義と言っていいんじゃない? >>866
すまんね
そこでポインタをアドレス値と言ってるから、そういう具体的なアドレス値に必ずしも対応するわけではないという説明
もちろん型の分類としてはpointer型でその通り >>865
Rust公式本のここが分かりやすい
Ownership Rules
・Each value in Rust has an owner.
・There can only be one owner at a time.
・When the owner goes out of scope, the value will be dropped. >>868
具体的なアドレス値を利用していない場合に限っては最適化で消えることはあるけど
&Tも&mut Tも取り出したいときには中身のRaw Pointerの具体的なアドレス値を取り出すことはできるんだから
参照にポインタの存在を仮定してもなんの問題もなくない? >>871
アドレス値として取り出すこともできるのはもちろんその通り
しかしRustの参照の概念を初心者に教える入門でアドレス値を仮定した教え方は完全に間違いです Safe Rustの範囲内で raw pointer として中身のアドレス値を取り出すこともできるものを
アドレス値を仮定してはいけないと言われてもお前の個人的思想なんかシラネー 俺も最初の頃は参照をアドレス値ポインタだと誤解してた
そしてRustではちょっとしたことまでわざわざポインタ経由でアクセスするのかよ無駄だなあと思ってたら
プログラムの生成コードを見るとアドレス値ポインタを使わずにちゃんと処理してた
参照はアドレス値ポインタを仮定しないもっと概念的なものであると今はわかるのうになった むしろC言語の知識があるRust初心者には
「参照はコンパイラが有効性をチェックする安全なポインタ」くらいの方が伝わりやすいと思う
メモリ上に値が保持されるならアドレスは存在するわけだし最適化でメモリにすら乗らないケースの方が特殊でしょ 参照経由でアクセスしてもポインタ使わずに済むときはポインタ出て来ないから
参照はポインタではなくアドレス値が必要になった時だけポインタになるものじゃないかな
ポインタになる場合も含むもっと上位な概念かな参照は 普通に公式がポインタだっつってんだからポインタなんだよ
お前の脳内ポインタ定義はどうでもええよ >>877
正しい
そしておまいはこのスレにしては珍しく聡明でレスも簡潔 もうこの言語だいしゅき、ソートアルゴリズムの美しい実装でこだわり過ぎて目の下に幕ができそう♡ ポインタ云々より「参照にも所有権がある」と言われたら初心者にとってはわかりにくいでしょ
所有権やそれに関連するルールを概念レベルで説明する際に
不必要に実装の詳細に依存すべきじゃないと思うよ このスレだったっけ?
以前クイックソートの実装でHaskellが最も美しいかどうかでレスバしてたのは? >>881
公式ドキュメントに
・Each value in Rust has an owner.
・All pointers are explicit first-class values.
とあって、 Pointer types のひとつとして References (& and &mut) が紹介されてんだから
参照にも所有権があるのは実装の詳細じゃなくて定義の問題だよ
参照はポインタの一種であり、ポインタは値の一種であり、すべての値にはownerがいるんだよ
公式ドキュメントで、実装ではなく公式ドキュメントでそう定義されてんだよ
最適化でポインタとしての実体が消えるとかいうのこそ実装の詳細に依存した話だよ The bookだと参照はポインタに似てるけどポインタとは違うと書いてあるね
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html
A reference is like a pointer in that it’s an address we can follow to access the data stored at that address; that data is owned by some other variable.
Unlike a pointer, a reference is guaranteed to point to a valid value of a particular type for the life of that reference. >>884
もっかい貼るけど公式リファレンスね
https://doc.rust-lang.org/reference/types/pointer.html
Pointer types
All pointers are explicit first-class values. They can be moved or copied, stored into data structs, and returned from functions.
References (& and &mut)
(中略)
Copying a reference is a "shallow" operation: it involves only copying the pointer itself, that is, pointers are Copy.
公式リファレンスのIntroductionね
This book is the primary reference for the Rust programming language.
the primary reference って書いてるんだから食い違ったらどっちを参照すべきかわかるよね A reference is like a pointer in that it’s an address we can follow to access the data stored at that address;
The Book でも参照はアドレスだって言ってんじゃねーか! >>867
ではownerの定義はどこに?
何がownerになり得る?
ぱっと見ownerは変数だよと言えそうな気がするけど、temporary valueは束縛されないのでルールの例外になってしまう
MIRレベルだとtemporary valueも変数に束縛される(あってる?詳しくない)ならMIRレベルの変数がownerと言えるかもしれないけど、the Bookごときがそこまで考えて書いてると思う?
さらにRc<T>/Arc<T>はstd docやらnomiconやらで"shared ownership"なる表現を平気で使っていて、
そのOwnership Rulesを絶対視するなら2番目のルールに全力で中指突き立ててることになっちゃう、よね?
もちろんTを共有しているよって意図は自然言語から分かるんだけど、その所有は変数への束縛とはまた別だし、
術語としてのowner/ownershipの定義は曖昧に思えちゃうのよね >>885
それはポインタ型だな
参照が実際に受け渡される時は参照値となってポインタ型になるのは当たり前 >>883
>・Each value in Rust has an owner.
↑これはThe Bookという初心者向けチュートリアルに書いてある概念レベルの話
>・All pointers are explicit first-class values.
> Pointer types のひとつとして References (& and &mut) が紹介されて
↑これらはリファレンスに書いてある実装レベルの話
後者は言語使用者に現在の実装を正確に伝えるためのもの
前者とは目的も抽象度も違う
The Bookでは参照自体をvalueと呼ぶことを意図的に避けてる
by valueといえば&Tや&mut Tは含まないTを指すように区別して書かれてる
初心者にとってのわかりやすさと実装を反映した説明の正確さを両立できない事情があるから 実装依存の悪口を言うだけの方がじつはわかりやすいんだよね
建設的な造語や新ルールを提案したりしないから >>887
これも同じで初心者にとってのわかりやすさと説明の正確さが両立しないのでお茶を濁してる点
ownerを変数だけに限定してしまうといろいろ困る
‘static &strのownerとか
変数には限られない何かownerという概念的存在がいるんだ!!!ということで理解しといて >>883
それは値に所有権があるだけだぜ
そしてそれが参照の時はその本体より長生き出来ないんだから
「参照の所有権」なんて分かりにくい言葉は出す必要がない
それだと>>843の
>原本および仮の所有権がすべて放棄された時にオブジェクトは破棄されます.
と発想が同じ >>889
>↑これらはリファレンスに書いてある実装レベルの話
>後者は言語使用者に現在の実装を正確に伝えるためのもの
お前の妄想は知らねえよ
https://doc.rust-lang.org/reference/introduction.html
This book is the primary reference for the Rust programming language.
現在の実装じゃなくて言語としてのRustについてのリファレンスだよ 参照はそれが具体的な値としてやりとりされるときはアドレスになるのはわかるけどさ
参照とアドレスを同一視する教え方はよくないね
あまりにも実装寄りの考え方 >>893
で、そのprimary referenceには所有権の定義はないんですよね?
なぜなのかな? >>891
> 変数には限られない何か
そんな定義があってたまるか 最適化をかけると生成コード上では実体としてのポインタが消えたり消えなかったりするとか言ってる奴が
リファレンスの定義の話をしてる奴を「あまりにも実装寄り」とか言い出すのギャグだろ 値の所有権は所有する変数のスコープで尽きるけど、
参照値の所有権は参照先の値より長く生存できない点でちがっていて特殊
だから入門書のそこで参照の所有権という言葉を持ち出すのは混乱の元かな >>852
それは突っ込みどころを間違えてる
仮の所有権を参照の所有権と言い換えても
「原本および参照の所有権がすべて放棄された時にオブジェクトは破棄されます.」
となって間違いが悪化してしまう >>899
それはそうだね
俺は「仮の所有権」を「参照の所有権」に言い換えろなんて言ってないけどね >>899
キミは頭悪いんやから黙っとき
これはイヤミじゃなくて助言な >>900
では言い換えるでもなく訂正するでもなく改善案となっているわけでもなく何をしたかったんだ?
唐突に頭悪いとか言い出すのは異常者か >>902
「仮の所有権とか言われても困る」という感想を書きたかったんだよ
訂正や改善案を出したいなら5chじゃなくて著者の問い合わせ先とか出版社の編集部とかでやった方がいいんじゃない?
あと >>901 は別人ね、お前が信じるか信じないかは知らんけど 仮の所有権と書かれているのは借用のことを言いたいんだなーってわかる気がする
でもそこで参照の所有権はちょっと違うような違和感 参照に所有権? それはそれで当然あるやろ
ってとこまで到達してる人と、できていない人 参照することと所有することを区別するのをやめやがったのがJava
これを思い出せる人と記憶がない人の認識がずれてるんじゃないの >>905
それ以前にオブジェクト(値)が破棄される話で参照の所有権は一切無関係やん 参照型の変数がスコープから外れたらただ単に「参照の値」がドロップされて参照先の値には影響を与えない
という話と
>原本および仮の所有権がすべて放棄された時にオブジェクトは破棄されます.
の記述は普通に関係ある話だけどね 所有権の複製オジに加えて
仮の所有権オジが出てきて
ある可能性が出てきたな
人類にはRustはまだ早かった説
Rustを正確に使うどころか正確に理解することすら困難だった説 GC言語では参照が生きてる限り参照先が消えないので意味があるけど
Rustで参照の所有権は意味がないよ 参照型の変数がスコープを外れたら何が起こるかって話だからRustでも意味あるよ なんで明らかに理解できてないのにこんな自信満々なんだろうな
正直そのメンタルはほんの少しだけ羨ましいわ >>910
意味のあるなしとかいう話かこれ?
参照にも所有権があるっていうだけの単なる事実でしょ
こだわるポイントが頭悪すぎない? Rust初心者が参照にも所有権があるんだ!普通の値と同じようにスコープを外れるまで所有権があるんだ!と騒いじゃう気持ちは分かるがもちろん無意味
普通の値ならばブロックスコープを外れるまで移動しない限り所有権があるのはもちろんだが
参照の場合は全く違っていてNLL (Non-Lexical Lifetime)により参照が入っている変数が普通の値なら所有権を持ちつづけている状況でも参照の場合はは無効に成り得る
つまり参照の所有権というものを考えたとしてもその格納されている変数のブロックスコープの間に所有権が保たれる保証はなく参照の所有権を考える意味がないのだ
これが普通の値の所有権との決定的な大きな違い
さらに加えて参照は参照先の生存期間を超えられないルールもあるためこの観点からも参照の所有権を考えても単独存在は出来ず意味がない
したがって参照の所有権なんてことを自慢気に言い出す人はまだRustの基本を理解できていない 参照についてだけわざわざ所有権に意味があるとかないとか考える意味がない
参照の値は参照先の値よりも先に破棄されなければならない
という追加のルールはあっても、所有権や値にまつわるルールの例外であるわけではない
参照は値のサブセット
すべてのvalueにはownerがいて、ownerがスコープを外れたらdropされる
referenceも例外ではない >>914
お前が所有権とライフタイムの区別がついてない初心者なだけやん…… >>916
ライフタイムが終わった時点で所有権も失う
所有権だけ単独で存在し続けられない >その格納されている変数のブロックスコープの間に所有権が保たれる保証はなく
コイツ所有権を「アクセスする権利」とかそういう感じで捉えてるのか
仮の所有権おじさんと同類じゃん
ownership って
値に対して ownership を負ってる奴はスコープから外れるときに値をdropしなきゃいけない
って責務の話であってアクセスする権利とかじゃないから保たれる保証がどうとか的外れだよ PhantomData<&'a T>は参照を所有しているに含まれますか? あと二ヶ月くらいであの本が世に出るのか
胸が熱くなるな(野次馬根性) >>918
アクセスする権利なんてひとこも言っていない
妄想で叩き出すのは人として外れている
あと参照&T &mut Tに特別なdrop処理はない
だから参照のdropを考える必要はない >>921
アクセスする権利とかそういう類のものじゃないから「所有権が保たれる保証」とか的外れだし
特別なdrop処理があろうがなかろうが値はdropされるし
考える必要がなかろうが参照もownerがスコープをはずれたときにdropされるよ
dropされるのはただの事実だよ、ただの事実なので考える必要があるかないかはどうでもいいよ >>922
普通の値の入った変数は他へ移動しない限りブロックの最後まで所有権があることが保証され最後まで自由に使うことができる
参照の入った変数はその参照が使われなくなったら無効となる使い捨てなのでブロックの最後まで所有権がある保証はない
例えばlet a = &T; f(a); let b = &mut T; g(b); let c = &T; の時点でもうaとbは使い捨てされており無効となり使えない
このように参照の場合は普通の値の場合と全く異なる
そして参照は使い捨てされてもdropで何か特別な処理があるわけではないため参照の所有権を考える意味がない >>923
>所有権があることが保証され最後まで自由に使うことができる
>無効となる使い捨てなのでブロックの最後まで所有権がある保証はない
>無効となり使えない
・ブロックの最後まで使えるかどうか
・drop時に特別な処理があるかどうか
どちらも所有権の話じゃないね
ownership とは「値の owner がスコープから外れたら値はdropされる」というルールです
そのdrop処理に特別な処理が追加されているかとか、ブロックが終わる前に無効になるとか関係ないです 参照に限らずCopy型はownership意識する機会が少ないからな
関数に参照の参照(&&T)を渡すみたいな例を考えないと参照値がborrowされた状態を作れない
>>923
「ブロックの最後まで所有権がある保証はない」ってあるけど
仮にブロックの最後で所有権がなくなってるような状況でブロックの最後にその参照を使う処理を追加したときに怒られるのは
ブロックの最後で参照を使った追加箇所じゃなく途中で参照先の値を横取りしようとした部分でしょ
これは「ブロックの最後まで所有権があって最後まで自由に使うことができる」ことにならないかな 参照をフィールドに含む構造体が入った変数はブロックの最後まで自由に使えることは保証されてないけど所有権あるよね^^; >>924
理解できてるけど意固地になっているだけか
参照の場合はNLLでスコープがブロックではない見えないスコープとなり得て使い捨てできるから参照ルールを満たしてる限り前段は自由となるし
後段のdropは何も特別処理がないし
わざわざ参照の所有権を持ち出してくる意味がないことを理解できてるようだな
>>925
そこは明記が不十分で誤解させたようでごめん
参照先の値の所有権ではなく参照自体の話だよ
例えばそこの変数aのこと https://doc.rust-lang.org/reference/destructors.html
スコープの定義を探してこのページに来たが、ブロックの途中でmoveされた変数のスコープの終わりはどこなのか
ふつうに解釈するとブロックの終わりのようだが
"When an initialized variable or temporary goes out of scope, its destructor is run, or it is dropped."
これは「スコープを抜けたらdropする」じゃなくて「スコープを抜けたらdropされていなくてはならない」という制約の表現という解釈が正しいのかな
じゃないと二重解放にすることになっちゃう >>927
お前が言ってる
・drop時に特別な処理がない
・ライフタイムの制約があり、ブロックの最後まで使える保証がない
は参照だけじゃなくて「参照をフィールドに含むCopy構造体」についても同じ挙動なんだけどね >>926
なんだちゃんと基本を理解できていないのか
構造体のフィールドに参照を持たせる場合は構造体にライフタイムパラメータが必要となる
したがって参照をその構造体に入れた後にその参照が使い捨てされた場合(例えば後から可変参照を使う場合)はその使い捨て参照と構造体は運命を共にする
ブロックの最後まで所有権がないことはその構造体にimpl Dropした時だけコンパイルエラーなることから分かる >>930
参照をフィールドに含む構造体については普通に所有権を考える意味があるし、
それとまったく同じ挙動である参照についても所有権を考える意味があるね
終わりだね >>928
いや、この解釈もだめだ
ローカル変数をmoveで捕捉したクロージャを返す関数は関数のブロックスコープを抜けてもまだdropしちゃいけない >>931
え?
参照を所有しても使い捨てになるから参照の所有権を考える意味がない話だぞ >>933
スコープを抜けた時に束縛されたdropする責務があることを「所有権を持つ」って言うから
所有権の話にその値が使い捨てかどうかとかライフタイムの制約があるかとか考える意味があるかとか関係ないよ
参照を束縛した変数も、参照を保持する構造体を束縛した変数も、プリミティブ値を束縛した変数も、
drop時に特別な処理があってもなくても、ライフタイムの制約があってもなくても
いずれについても「スコープを抜けた時に束縛されたdropする責務がある」ので所有権はあるよ
意味があるとかないとかどうでもいいよ
参照を束縛した変数も、参照を保持する構造体を束縛した変数も、プリミティブ値を束縛した変数も、
いずれについても「スコープを抜けた時に束縛されたdropする責務がある」ので所有権はあるよ >>928
https://doc.rust-lang.org/reference/glossary.html#initialized
> Initialized
> A variable is initialized if it has been assigned a value and hasn't since been moved from.
一番最初に重要なことが書いてあったやつだすまぬ
つまりmoveされたらその時点でdrop scopeがなくなる=drop責務から解放される=所有権を失うと言えるわけね 意味ないニキ(ID:sdtPy+yt)はもう
自分で何言ってんのか分かってなさそう >>930
ホントにそんなこと起きるのか疑問なので試してみたらマジにそうなった
誰かこうなる仕組みを教えて~
参照をメンバーに持つ構造体で実験
struct Foo<'a> {
x: &'a String,
}
fn main() {
let mut s = String::from("abc");
let mut foo = Foo { x: &String::new() };
foo.x = &s;
println!("{}", foo.x.len());
s.push('0');
println!("{s}");
}
ここまではコンパイルも通り動いた
参照を構造体メンバーに格納した後に
可変参照を利用となるpushをして普通に動いた
そこで書かれてる通りに構造体にDropを実装
impl<'a> Drop for Foo<'a> {
fn drop(&mut self) {}
}
するとコンパイルエラーとなった
つまり参照の所有権は使い捨て(?)とやらで意味がなく(??)
それを持つ構造体はブロックスコープの最後まで生きていないため(???)ということ??
Dropの有無でエラーとなる仕組みがさっぱり分からない >>938
dropが実行されるときにxが利用される可能性があるから
s.pushの時点ではfoo.x = &sのimmutable borrowがまだ有効なのでmutable borrowはできませんよ
ということ
Dropがなければs.pushの時点でfoo.x = &sが使われる可能性はもうないから
immutable borrowの期間は終わってるものとして扱える >>934
>参照を束縛した変数も、参照を保持する構造体を束縛した変数も、プリミティブ値を束縛した変数も、
>いずれについても「スコープを抜けた時に束縛されたdropする責務がある」ので所有権はあるよ
現実にはスタックポインタをインクリメント/デクリメントするだけなので
挙げられてるような種類の変数にdropする責務や所有権があるかどうかは
初めての人への説明としてわかりやすいかどうかという主観の問題だよね >>939
ふむむ
参照の所有権は自動的に打ち切られてしまい参照の所有権を考えても意味がないというのは正しいのですか?
impl Drop実装しなければdrop処理は無いと理解していいのですか? そうだろ
もちろん>>924の言う通り所有権は「値のownerがスコープから外れたら値はdropされる」というルールは常に適用できる
しかし参照については使い捨てができるためブロックスコープではなくルールを成り立たせるために見えない仮想的なスコープを用意してそこを外れたと考える本末転倒
そしてdropについても何もなされない
だからわざわざ参照の所有権を持ち出して来て考える意味がない
参照で重要なのは所有権ではなくmultiple readers xor single writerルール しかし、役に立たないことを知りたがる人間の方が
ある知識が役に立つ証拠が不十分と思ったら勉強やめる人間よりも安定しているかもしれない 所有権とライフタイムとDropトレイトを全部ごちゃ混ぜにして覚えちゃってる困ったちゃん 初心者になるのも大変だからなw
大体はそれ以前に転がされて終わるw 実用レベルの達する前に学習者の9割が脱落してしまう言語
言語がRustだけになるとIT業界は終わる模様 複オジも仮オジも無意味オジもみんなRustの被害者だった? 誤解して入門を終えて実用的なプログラムを書けないで四苦八苦するのは個人の問題だからいい
でも間違った知識のままそれを主張するのは非常に迷惑
ポインターのミスでヒープをぶち壊してるような感覚 >>947
その時はIT業界とRust業界に分かれるから大丈夫 「所有権はdropする責務」の出典だけ分かればすべて腑に落ちる それでようやく理解できた
参照はdropで何もしないから責務がなくて使い捨てしても構わなくて所有権を考える必要ないと 次スレってワッチョイつけたりしないの?
意図せずともID変わりがちな人もいるし追い辛いからつけてほしい 参照についてだけ""わざわざ""、""特段に""、所有権について ""敢えて意識して""、""所有権について考えないようにする""必要性がない 付けてもいいけどたぶんそれだと困る人はワッチョイなしでスレを立てる
そしてそっちが伸びれば誰もワッチョイありスレを使わない
そうしてできた残骸がすでに3スレある
立てたければ勝手にすればいいけど、ワッチョイありスレだけを使い荒らしに反応しない強い意志が求められている ワッチョイ有無どちらのスレに書き込むかは各自の自由
ワッチョイ無しスレは立てたい人がいればこのスレの後に自由にすればよいが
ワッチョイ有りスレは既に3つもあるため4つ目を立てることは控えた方がよい IDコロコロ野郎に荒らされるよりはワッチョイでいいやろ ワッチョイあるスレは十中八九ネガティブ禁止の盲目信者専用になる ワッチョイありが盛り上がってたらそっちに行くって言ってるのに誰も向こうに書かないじゃん マイナンバーにたとえるとお金バラまかないと無理なのでは 自演対立や架空キャラ自演がやりにくくなるからこのままがいい 次スレをワッチョイにして統合すればいいんじゃないの? すまんが、The Bookと比較してオライリー本ってどんな内容になってるの? The Bookと比較すると
- 図や説明がわかりやすい
- カバーしてる範囲がやや広く説明も詳しめ
- C++との比較がやや多い
- IteratorやStringのメソッドの多くを一つ一つ説明しておりややくどい
- Rust特有のコードの読み方も書いてあって助かった (‘aはティックエーと読む等)
個人的には1点目がすごく大きかった
特にOwnershipとReferenceの章 オライリー本はRust始めた時に買ったけど一生積んでる Rcって値を所有してるの?
参照だから複数存在できる感じ?
規則と矛盾してて全く分からない >>970
同じ値を複数のRCで共有している。
Rustの場合、RCの「参照」は、参照と借用とか参照外しとかの「参照」とは別物と考えたほうが良い。 >>970
共同所有
Rc::cloneで新しい共同所有者を追加する
共同所有者がスコープアウトすると共同所有者リスト的なものから削除される
最後の所有者がスコープアウトする際に所有してるポイント先もdropされる これを?オペレータ使った書き方するにはどうすればいいですか?
if let [first, second, third] = vec {
...
}
これでは上手く行きませんでした
let [first, second, third] = vec?; >>973
マッチしなかった場合どうしたいのか?
そのコードが含まれる関数の戻り値の型は? Rustに限った話じゃないけど低レイヤーに関する情報ってなかなか入手できない
Rustなら一応Embedded Rustがあるけど実践的にはいろいろ足りてない >>973
このスレでErr値を使わない時にif letやlet elseを使う、とあったのと同じで
Err値がない場合も同じようにこうする
let [first, second, third] = vec else {
return Err(...); // or None
};
>>975
話が曖昧すぎるので具体的に
>>976
クレイピングは知らん
scraper crateを使っている 相変わらず複オジの会話の噛み合わなさとオレオレ用語の使い方は異常だな >let [first, second, third] = vec else {
> return Err(...); // or None
>};
vecを直接マッチさせることはできないからsliceにする必要がある
それはいいとしてもvecを固定長のarrayにマッチさせて各要素を異なる名前の変数に入れるかエラーにするやり方はcode smellに感じる
vecが外部入力で変更しようがないのかもしれないがそれでも素直に長さチェックしたほうが良い >>980
スライスパターンとそのマッチングはRust公式リファレンスにも載っている普通の便利な用法
わざわざ長さチェックしていたらスライスパターンマッチングを知らない初心者かなと思ってしまう
さらに..パターンも併せて例えば
if let [.., ラス前, _] = s
これも公式に例として出ていて同様に長さチェックは不要 >>973
moveしたいんなら
let [first, second, third]: [_; 3] = vec.try_into().map_err(|_| MyError::InvalidNumberOfHoge)?;
Vecを[T; N]にtry_intoした場合
Errの型がVecなのでmap_errしないと?が使えない referenceでも同じ
let [first, second, third]: &[_; 3] = vec[..].try_into().map_err(|_| MyError::InvalidNumberOfHoge)?; 結局?を無理に使うよりこの方が短く可読性もいいな
let [first, second, third] = vec[..] else {
return Err(MyError::InvalidNumberOfHoge);
}; そんな使い方が許されるのは個人プロジェクトか使い捨てのコードだけだね ログファイルの各行をsplitして特定の項目(例:日時とIPとURL)だけを拾う処理とかで使えそう
Rustでは書いたことないけど >>986
Rustのリファレンス書の例>>986を許さない人こそ偏ってね?
そういう偏執者は代わりのコードどう書くんだ? >>986はいつもの荒らしだから無視しろ
荒らしは文句をつけるだけで修正案を出さない(出せない)から見分けられる >>987
使い捨てのコードじゃなければそういうのはcsv parserやargument parserを使って構造体に変換するからパターンマッチでは書かないよ それは用途によりけり
構造体にDeserializeする場合もあれば構造体を用意しない場合もある
パース用途以外でマッチングならパーサーが出て来る余地すらない このスレッドは1000を超えました。
新しいスレッドを立ててください。
life time: 53日 0時間 8分 30秒 5ちゃんねるの運営はプレミアム会員の皆さまに支えられています。
運営にご協力お願いいたします。
───────────────────
《プレミアム会員の主な特典》
★ 5ちゃんねる専用ブラウザからの広告除去
★ 5ちゃんねるの過去ログを取得
★ 書き込み規制の緩和
───────────────────
会員登録には個人情報は一切必要ありません。
月300円から匿名でご購入いただけます。
▼ プレミアム会員登録はこちら ▼
https://premium.5ch.net/
▼ 浪人ログインはこちら ▼
https://login.5ch.net/login.php レス数が1000を超えています。これ以上書き込みはできません。