Rust part12
■ このスレッドは過去ログ倉庫に格納されています
前スレで循環参照なんて初心者でもすぐ作れてしまうとの意見もあったけど
Rc型を使うだけの初心者には原理的に不可能
内部可変性を与えるRefCell型も併用しないと循環参照は作れない
一つの可変参照&mutか複数の不可変参照&のどちらかのみ許されるコンパイル時確認ルールを実行時確認ルールで行なうのがRefCell型
それにより明示的に可変参照を得ることで内部可変性を持てるようにしたもの
そしてRcと組み合わせてRc<RefCell<T>>を使うことで既にあるリンクを書き換え出来るようになりようやく循環参照を作れる
もちろん自分でunsafeすればやりたい放題てすがunsafeせずともメモリ安全性ルールの元に使えます
ここまで出来る人なら強参照Rcだけでなく弱参照Weakも併用できますから循環参照を避けることも出来るでしょう マーフィーの法則
失敗する余地があるなら、失敗する テンプレートがやたらたくさん入り乱れる言語は嫌いだわ
可読性めちゃくちゃ悪いよな >>7
そこまでできてやっとRust初心者という見方もあるかもしれない 直接関係ないけど、RefCellは借用チェックが実行時段階でのチェックになって
しまうんだってな。 RefCellの借用チェックが実行段階にまで持ち越されるのは、循環参照とは全く別の話。
それはつまり、「ゼロコスト」ではないということ。 >>7
Rc<RefCell<T>>じゃなくてRefCell<Rc<T>>だね
気持ちはよく分かる >>14
どっちもあるけど通常はRc<RefCell<T>>だよ >>983
なるほどサンクス
リージョン理論に線形論理を上手く組み合わせて、cycloneとかの欠点を克服したrustってすげーなあ
とはいってもそもそも二重開放してエラーになるというのがピンとこない
free(a);
free(a);
は二重解放しているように見えて合法だろ?
一度目のfreeでaにNULLが代入されて、二度目のfreeでは引数がNULLの場合はそのままreturnって処理されるんだから、理論上は何度free使ってもエラーにならないじゃないか >>16
なぜRustのスレに書いているのか不明だけど
当然NULLにならなかったよ
キャスト警告無視で
#include <stdlib.h>
main() {
char *ptr = malloc(1024);
printf("ptr(1): %x\n", ptr);
free(ptr);
printf("ptr(2): %x\n", ptr);
free(ptr);
printf("ptr(3): %x\n", ptr);
}
実行結果
ptr(1): ea8f72a0
ptr(2): ea8f72a0
free(): double free detected
core dump >>11
静的にチェックできないような使い方を実現するためのものなんだから
実行時チェックになるのは当然では
オーバーヘッドが気になるなら UnsafeCell を使えばよい
>>16
C には参照はないので free(a) という呼び出しで a の値が書き換わることはなく malloc で獲得した領域のアドレスがそのまま残る
領域自体は free で解放済みになっているため、アクセス (free 呼び出し含む) した場合何が起きるか分からない
このような解放済みの領域へのアクセスは use-after-free と言われるもので、脆弱性の原因として有名 >>18
なるほど
free内で代入されるのではなく
自分で代入しないとNULLにはならないのですね。
ありがとうございました。 uaf防ぐのってぶっちゃけ簡単ですよね?freeしたあとにNULL代入するのを鉄則化するだけですよね? ポインタコピーすることないの?
加減算することないの? int* p1 = malloc(sizeof(int));
int* p2 = p1;
free(p1);
p1 = NULL;
free(p2);
極端に書くとこうかなあ? >>22
1つのオブジェクトを複数のポインタで挿している場合にそういうわけにはいかない。 >>25
複数のポインタにそれぞれNULL代入したらいいだけの話ですよね?
難しい話ですか? >>23
加減算考慮するとなんかやばいんですか?
本当に素人なので教えてください!! >>24
p2=NULL;すればいいだけの話ですよね? >>27
プログラミングをしたことがなく
頭の中での思考実験もできないのか
プログラマーに向いていない 手で書くと効率は落ちないが、実行段階で自動的に NULL を入れようとすると
効率が落ちるという意味ね。時間とメモリーの両方で。
NULLが入っているか判定して error にするなどの処理も必要となったりする。 >>32
すみません。
おっしゃるとおりかも知れないです。。 >>36
参照カウンタ方式は、自動的に関連するポインタを巡ってNULLを入れるよりは
効率が良いのでプログラマーの裁量で選択できるようになっている。
しかし、それでも僅かに効率が落ちるのでなるべく使わないでプログラムする
方が良いと思うプログラマーが多いということだよ。 プログラマーならば、まず思考実験、それで解決しなければコーディング
その上で問題点を尋ねる
それをしない人はプログラマーではないから、このスレの邪魔 >>38
そんな僅かな効率の低下でも忌避されるんですね!!
わかりました!! 手作業で書く場合は、1つのオブジェクトを削除する場合、それを参照する
全てのポインタに NULL を入れるのは効率が良い。だから、多くのC/C++
プログラマはかつてそうしてきたし、今でもそうすることも多い。
ところが自動でそれをやるには静的解析は難しいので実行段階でも
余計なメモリーを追加で消費して行うことになる。それが効率が悪い。
そして、NULLが入っているかどうか分からないのでその参照を
使う場合には、いつも最初にNULLが入ってないことを確認してから
作業をする必要があり、その判定に 2 クロックくらいかかる。
この2クロックが忌避される。 >>7
言ってる事はかねがね同意ですが、プログラムが共同作業の為にRefCell<Rc<List>>とA氏が定義してあるものに
違う人B氏が何も考えずにRc::clone(&a)で循環を入れてしまう事は十分あり得るでしょう。
状況次第ですが循環参照を考慮してないA氏が悪いのか、Rc::clone(&a)でぶち込んだ人が悪いのかは要件次第ですが
これを簡単と言わずに(あなたは言っていませんが)難しいと表現するのは違和感がありますよ >>42
詳しい解説ありがとうございますm(_ _)m
メモさせていただきます。 >>37
嘘を教えたらあかんやろ
N人の人が自分を指しているとして
まずそのN人をどうやって知る? >>45
自力で数えればいいんではないでしょうか? >>40
まあ、そういうこと。
というのは、参照カウンタも、1つのヒープノードに対して必ず1つ必要になるので、
ノード数が多いときには膨大なメモリーの無駄になるから。
それとノード数が多いときに参照カウンタを 0 クリアしたり、1足したり
する作業も馬鹿にならないと考えるプログラマが多い。
なぜかというと、そういう自動化を行わなくても、人が頭で考えて手作業で
NULLを入れても十分に安全にプログラムできるプログラマが多かったからだよ、
少なくとも昔のCプログラマには。
手作業でやってもとくに危険を感じないので、効率を落としてまで自動化する
必要が無いと思われている。
手作業でNULLを入れるのは難しくない。
ところが、コンパイラが自動で効率を落とさずにそれをやるのはめちゃくちゃ難しい。
それは人間の脳がそれだけ優秀だということだよ。 >>45
手作業で人間が頭で考えたら、難しく感じないプログラマが多かった、という
ことだよ。
難しく感じるプログラマも居る。
数学の才能だと思う。 コピーしたポインタを別スレッドに転送とかしてたら、いつNULL代入すべきかすら分からんしなぁ >>48
ああ、自動化の効率面の話か。
凄く効率が落ちるわけではないよ。
リンクで辿るだけだから。 >>47
違うだろ
ある時点でNヶ所から指されているとして
そのNヶ所をどうやって知る?誰がどのようにNヶ所を管理する? >>51
静的解析はコンパイラには難しいが、動的なら簡単。
人間にも、「一般性を持って」は分かる方法は無い。 言っておくが現実世界では俺は天才だと言われているから、俺が簡単だと
思うことは、大部分のプログラマには難しいと思うかもしれないがな。 >>52
データ構造は入出力の変化に伴いどんどん変化していく可能性がありますから人間は覚え追いきれないよね
さらに一つのmalloc毎にそれぞれそこをポイントしている利用者の数も利用者数も異なりますよね
それらのデータをどこでどうやって管理するつもりですか? まず前提が誤りです
変化を追えば覚えられます
思考実験できないんですか? >>56
利用者リストを覚えておくためにメモリが必要やね
それもmallocするか
mallocで取ってきたメモリ管理のために更にmallocが必要となってしまったぞ
利用者が増えてきたら利用者リストの領域が足りなくなる
またmallocするか
全てにNULLを入れるためだけに大がかりになってきたな
この方法は本当に大丈夫なのか? データが育っていったら100か所からリンクされるとか普通によくあるわけだが
その100か所にNULLを代入しにいくために100か所の場所をリストで持つのか
しかもmallocした各データ毎にリストを持つのか
全てにNULL代入はあきらめた方がいいんじゃね? アクセスするとたちまち例外発生するぬるぽにはどんなデータが格納されているのだろうか int* p1 = malloc(sizeof(int));
int* p2 = malloc(sizeof(int));
int* p3;
if (なんかの条件) p3 = p1; else p3 = p2;
free(p1);
p1 = NULL;
// p2はまだまだ使う
こういうときp3はどうするのかっていう すべてのヒープを**ptrで管理すればできなくもない気がする
参照するたびに有効かチェックしないといけないから激しく面倒だけど >>61
なるほど
参考になります
回答してくださってありがとうございました。 >>62
その**ptrの格納領域の確保がデータ事に発生
しかも伸長してしまいますね https://twitter.com/cpp_akira/status/1430779310885859330
最近はC++の発表資料を公開すると「Rustでいいじゃん」というコメントがたくさんつくのか…。Rustへの言及とか一文字も書いてないのに。
普及活動だと思うけど、さすがに嫌がらせチックに見える。
↓の資料公開した時の話っぽいんだけど、Rustでいいじゃんって言われてるソースが見つからない・・・
https://speakerdeck.com/faithandbrave/opunhua-gajin-muc-plus-plus-falsexian-zhuang-tozhan-wang
言われててもおかしくないとは思うけど、実際のところどうなんすかねえ
アロケータ回りがまだ弱いから少なくともゲームは10年後もC++のが強そう
https://twitter.com/5chan_nel (5ch newer account) >>27
まだ利用者がいるのに全てへNULL代入しにいく時点で破綻
つまりまだ自分以外の誰かが利用中かどうかを知らないといけない
つまり利用者リストを管理する必要がある おまえらスレチがすぎるぞ
まとめてどっかいってくれ >>66
Rust はメモリ安全性を保証することが論文で示されているので
どうしてもC++でないと出来ないこと(=まだRustが対応してないこと)であることを示さないと
「Rustでいいじゃん」となってしまうのは仕方ないかも Rustは言語仕様が未確定なのが懸念事項かな
実用上は大して困らないからどうでもいいんだけど
期待してたRust Foundationは何故かディレクター探し始めてるしw >>70
2018editionに2019のasync/awaitゼロコストサポートで言語としては完成でしょ? >>70
それを期待するならFoundationよりこっちかな
https://ferrous-systems.com/ferrocene/
2022年末にASIL-B認証が目標 >>71
まだOpenなRFCいくつあると思ってるんだ でもSchemeとか言語仕様も形式意味論も定まってるし とりあえず
https://doc.rust-lang.org/stable/reference/
のWarningを消してくれるだけでいいんだ
2018版だけでも確定できないのかな 確定できないというよりメンテする人がいないんじゃないかな
言語仕様に詳しいけどコード書くよりドキュメント書きたいっていう奇特な人が要求されるわけで ferroceneみたいに具体的な目標があって企業が投資してるなら進むだろうけど
リファレンスの更新とかあまり需要もないじゃない? 第三者が処理系を作れるレベルで言語仕様が固まれば普及が進むと思うんだけどね
Rust Foundationにはそういう方向性を期待してたけど想像以上に何もしなかった Foundationはもともと裏方に徹する組織だと言ってたし
その通りに動いているのでは
最近だとgccrsのライセンス絡みの知財チェックとかやってたかと 言語仕様が固まるってのは新機能の追加をやめるということ? 言語仕様とコンパイラのバージョンが一体化しているようではミッションクリティカルな領域で採用されない >>84
厳密に運用するなら機能を追加するときは言語仕様のバージョンも上げる
固めるのは言語仕様の1つのバージョンってだけで言語の進化をとめるわけじゃないよ >>84
現状では「今そのように動いている処理系」と「処理系の (不十分な) ドキュメント」がある状態で、
それのどこからどこまでが言語仕様として満たすべき要件なのか保証されている動作なのかがわからない。
追加するも何も今の時点でどうなっているのかわからん。
どこかにはあったりもするんだろうけど、開発者向けの議論の中などに分散していて「仕様書」としてまとまってないんだ。 安全性は型システムによるところが大きいから、仕様書が無いと使えないというのは無いと思うがね コンパイラだってバグがない保証なんてないんだから
テストするしかないね LLVMの中間表現に依存してる時点で、言語仕様をまともに設定するのは無理。
だから他のコンパイラが作られることもない。 仕様書、仕様書言っている人はOSやライブラリのAPIとかシステムコールとかはどう考えているんだろうね
MSDNだって盲信できるほどの信頼性はないし、OSS系なんてソースコードが仕様書状態だろ
もっと言えば処理系だってオプティマイザの仕様などは文書化されていなく、実際に調査しないと判らない事も多いよな こんな状態じゃc++の置き替えなんて夢のまた夢だな。 完全な仕様書wがないのが問題だ思うなら使わなければいいだけ
クッソ無駄なやりとりでスレ消費すんな 少なくともC++はもっとずっと細かな仕様が書いてある。
例で説明する時でも、変な例で説明せずに数学の教科書の様な粒度の細かい例で
説明されているで、数学の教科書の様な雰囲気を持っている。
Rustの場合、ライフタイムの説明をとってみても、ちゃんとした数学的説明になってない。 時々プログラミングは文系でも出来るという人が居るが、そういう人には、
数学的な説明とは何かがぴんと来ないかもしれないので、良い仕様書も
書けないかもしれない。Rustのbookの著者ももしかしたらそうなのかも。
本人は頑張って書いているつもりでも数学的な論法になってない。 高校数学レベルで言えば、ベクトルの和の定義は図で例を使って説明されるが、
3つの矢印で、c = a + b が説明される。
これは最も粒度が細かい説明で、これ以上簡単に説明できない。
ところがRustのライフタイム注釈の説明は、説明に使われている例が不適切で
とても長いが本質が説明し切れてない。
本来は、「ライフタイム注釈とは何か」を数式や擬似命令などを使ってせいぜい2ページ以内で
説明できるはずだ。
ところが実際の説明は、変な例を使って長々と説明されている。
数学的な説明になっていないので応用が効かない。
数学的説明とはほとんどの場合「パターン」だ。パターンになっているから応用が効く。
パターンとして説明されてないと応用が効かない。 コンパイラが一つしかない状況は現状メリットでしかない
C++のようなレガシー言語の轍を踏まない賢い選択 数学的説明にしたいなら、
「仮定」を置く。
「入力」と「出力」を明確にする。
「最も粒度の小さい例を書き、パターン」にする。
集合論や論理学の「かつ」「または」「積集合」「和集合」「背反集合」「区間の積」
などの言葉を使って書く。
ところが、Rustのbookはこのようなことが全く出来てない。 >>98
仕様書が数学的(パターン的、自動機械的)に書いてないので、厳密な
仕組みや仕様が分からず、他の人がコンパイラが作りにく。
だから、「Rustは実験してみないと分からない」。
ところが、CやC++やRuby,Java,C#,Pythonなどは実験しなくても分かる
ようになっている。それはなぜかというと、仕様が粒度が細かく説明されて
いるから、パターンになっており「パターンの一部への代入」や「当てはめ」
が出来るので頭の中だけで全てプログラミングできてしまうから。 ■ このスレッドは過去ログ倉庫に格納されています