Rust part11

■ このスレッドは過去ログ倉庫に格納されています
2021/06/17(木) 00:24:12.56ID:NvYoNP9C
公式
https://www.rust-lang.org/
https://blog.rust-lang.org/
https://github.com/rust-lang/rust

Web上の実行環境
https://play.rust-lang.org

日本語の情報
https://rust-jp.rs/

※Rustを学びたい人はまず最初に公式のThe Bookを読むこと
https://doc.rust-lang.org/book/

※C++との比較は専用スレへ
C++ vs Rust
https://mevius.5ch.net/test/read.cgi/tech/1619219089/

前スレ
Rust part10
https://mevius.5ch.net/test/read.cgi/tech/1617367084/
2デフォルトの名無しさん
垢版 |
2021/06/19(土) 01:15:49.18ID:3FFA7ImF
>>1 おつ
3デフォルトの名無しさん
垢版 |
2021/06/19(土) 01:20:06.41ID:VXoz87sA
>>1
4デフォルトの名無しさん
垢版 |
2021/06/19(土) 02:14:18.53ID:tZhqlEYm
勉強中でいまいちよくわかってないんだけどさ
よくRsutでメモリの扱いが安全になるとか言われてるけど、これって解放忘れを防いでくれるだけであって、オーバーフローを防いでくれるものではないわけ?
それとも登場する全ての型がオーバーフローしないような仕組み(スタックプロテクター以上の何か)があるの?
2021/06/19(土) 03:26:44.04ID:5peZoltk
>>4
型のオーバーフローっていうのは算術オーバーフロー(桁あふれ)のことかな?
であればここを読むといいと思う
https://doc.rust-lang.org/book/ch03-02-data-types.html#integer-overflow
2021/06/19(土) 04:22:00.60ID:/f53/cxR
スタックプロテクターの話題を出すってことは
バッファオーバーフロー (バッファオーバーラン) のことじゃないかな。

配列は大きさの情報を持っているし、
配列の一部の範囲を受け渡すときはポインタでなくスライスで扱うのが Rust の基本的な設計になってる。
ポインタと違ってスライスは範囲の情報を持っているのでチェック可能で、チェックする仕様になってるよ。
溢れたら panic する。
(もちろん unsafe な操作をしたらいくらでも危険な操作は出来る。)

絶対に溢れないことがコンパイル時に見抜ける場合であれば
チェックしないように最適化したりすることもあるし、
チェックする場合でも現代的な CPU ではほぼ確実に分岐予測が成功するから
処理速度が遅くなる分は十分に小さいとかいう話があったはず。
2021/06/19(土) 09:47:40.12ID:5peZoltk
バッファオーバーフローのことなのか
Safe Rustでは基本的に発生しないが仕組みというより
unsafeなコードを書く人が要求された安全性を保証するという約束の上に成り立ってる

Rustの要求するメモリ安全性を保証するためには
unsafeなコードでポインタをdereferenceする前にout-of-boundsかどうかのチェックが必要
2021/06/19(土) 14:15:48.54ID:lGsmv2n4
境界チェックなんて他の言語でもあるし、別にそこがRustの特別な強みではないんだよな
それよりは、

> これって解放忘れを防いでくれるだけであって

だけじゃなくて、use-after-freeとか、
思わぬ箇所でオブジェクトが変更されることによるデータ競合とか、
をコンパイル時にチェックできるのが強い

詳細はThe Book 4章に
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html
日本語版はこっち
https://doc.rust-jp.rs/book-ja/ch04-02-references-and-borrowing.html
2021/06/19(土) 15:41:11.20ID:7Y2aa0wT
解放忘れ(メモリーリーク)はRustは
保証してないのでは?
2021/06/19(土) 16:32:09.25ID:+A5T+Cz1
Rc/Arc以外でメモリリークって起こるの?
2021/06/19(土) 16:52:09.35ID:5/JSw/kX
Box::leakして返された参照を捨てるとメモリリーク起こせる
2021/06/20(日) 02:45:16.68ID:O2AvtKTb
最近勉強始めたんだが、正直ムズイ

特にwinapiのポインタ引数が(構造体のポインタではなく)DWORDで定義されてたりするので、
キャストするのが超絶面倒臭い
microsoftはcrate修正してほしい

まあ、rustというよりwinapiの問題なんだが……
2021/06/20(日) 02:57:43.41ID:h62I7Iw3
わかる。
DWORD とポインタをカジュアルに同一視する API はまだマシなほうで、
Rust は文字列をスライスで扱うから単純にポインタに変換してもヌル終端されてないのがクソめんどい。
文字列を渡すとかすごく普通にあることなんで、それがこんなに面倒くさいの勘弁して欲しい。
14デフォルトの名無しさん
垢版 |
2021/06/20(日) 06:34:35.27ID:9R7FlmLP
普通に書いててwinapiとか使う機会ないと思うけどOS機能を直接触る必要のあるライブラリでも書いてるのかな?
2021/06/20(日) 15:00:11.94ID:ZAMdhkls
OS層に近いAPI全く使わないならrustやc++とかデメリットの方が多いような。
16デフォルトの名無しさん
垢版 |
2021/06/20(日) 16:38:08.85ID:K2CzG+CP
俺も最近Rust勉強してるんだけど、GC無しでのメモリ管理が最高に気持ちいい
何もかもRustで書きたくなる
2021/06/20(日) 17:33:03.51ID:D+WmuL2+
ワイは基本moveってところが気に入ってる
参照をもって回るんじゃなくて
実態をmoveで渡してmoveで返されるとき清々しいのを感じる
2021/06/20(日) 17:51:48.27ID:BAitg4NO
システムコールや低レベルなライブラリをいい感じに安全にラップしてくれるcrateが提供されてるのはrustの良いところ
2021/06/20(日) 19:32:30.59ID:5UZIMOEC
moveって最適化ビルドだと消えたりしてるのかな?
2021/06/20(日) 21:04:33.39ID:BAitg4NO
moveが消えるとは?
moveがコンパイルされた結果のmemcpyなどが消えることはある
2021/06/20(日) 22:37:40.54ID:X7PAuK/l
>>13
Rustのスライスは、ほぼPascal文字列だから、Cよりも古くから作法や
概念は存在している。
しかし、なぜCがPascal文字列ではなく0終端文字列にしたのかには
理由があって、文字列の途中(部分文字列)を扱わない場合においては効率が良いから。
0終端文字列の欠点は、部分文字列を扱おうとするととたんに面倒なことになること。
ただ、strcmpみたいなものを書いたり、字句解析を書いたりするときには、効率は良い。
字句解析では決定性オートマトンの理論がグラフ的(状態遷移図的)になっており、
Cの0終端文字列とはとても相性が良い。
そして、コンパイラの実行時間の大部分は、実測してみると、意外にも字句解析が占めている。
字句解析は単純ではあるが、量が多いので1クロックの差がものをいう世界である。
ただ、Pascal文字列(スライス)が字句解析でも有利に働く場面はあるにはあるが。
どちらの方式が一方的に優れているとはいえない。
2021/06/20(日) 22:45:58.47ID:X7PAuK/l
>>21
すまん。今調べたら、Pascal文字列は、配列の先頭に文字数が
入っている特殊な形式で、スライスとはまた違うものだった。
今まで誤解していたわ。
Pascal文字列はダメだわ、全く意味無し。

ただ、俺が言いたかったのは、Rustのスライス方式も古くから概念自体は存在し、
Win32APIでもGetTextExtentPoint32()なんかが、ポインタと、その後に続く
文字数の両方を指定する方式を取っている。
このやり方は、文字列の中に 0x00 を埋め込まなくても部分文字列を扱えて
便利は便利。もしこれを部分文字列の場所を少しずつ変えていくようなの場合に、
0終端文字列でやろうとすると、効率が悪くなる。
ただ、いつでもスライスの方が0終端文字列より効率が良いという訳ではない。
それが>>21で言いたかったこと。決定性や非決定性オートマトンの考え方で字句解析を
する際には、Cの0終端文字列はスライスより効率が良い。
2021/06/20(日) 22:54:46.58ID:D+WmuL2+
読む価値無い文章をダラダラ書いちゃうのって
なんらかの障害なんやろな
2021/06/21(月) 00:08:08.49ID:85An+spJ
>>22
そのせいでPascal文字列は長さ255文字までに制限されてたのよな。
2021/06/21(月) 00:46:14.79ID:PP3lMGGZ
結論は、Rustがそんなにすばらしい言語とは到底思えないということだ。
2021/06/21(月) 02:03:12.76ID:wnQSc3ge
ええんやで
27デフォルトの名無しさん
垢版 |
2021/06/21(月) 03:36:25.54ID:JQDu6zSa
>>25 まぁ用途によるやろ
発展途上なところもあるし、そもそもRustが向いてないような用途もある
28デフォルトの名無しさん
垢版 |
2021/06/21(月) 07:39:35.72ID:3uERIKtL
>>13
様々な文字列処理をしたことがある人になら自明ですが
文字列を扱う場合は¥0終端よりもスライスのほうが圧倒的に有利です
例えば何段か深いディレクトリの絶対パスが与えられた時に各ディレクトリのリストを返す(つまりsprit)時
¥0終端方式だと元の文字列を書き換え破壊しない限りコピーが発生してしまいます
スライス方式だと書き換えもコピーも発生しません

これは正規表現によるパターンマッチングでも同じで¥0終端方式だと結果である部分文字列をコピーしなければ返せません
またHTMLやJSONなどの様々な構造データの解析結果でもそうです
JSON文字列を解析して内部構造化表現にする時もスライス方式ならば文字列のコピーが発生せずに済むわけです
2021/06/21(月) 08:53:58.31ID:xiUkcw19
>>28
>文字列を扱う場合は¥0終端よりもスライスのほうが圧倒的に有利です

有利だろうが何だろうが、APIや過去の資産を活用するのに面倒という事実は何も変わらないが
2021/06/21(月) 09:07:04.10ID:WbPJLyAM
そういやRustの std::ffi::OsString って¥0終端なんだっけ?
2021/06/21(月) 09:43:03.40ID:ILFJsIgR
>>30
違う
null terminatedはCString
32デフォルトの名無しさん
垢版 |
2021/06/21(月) 11:29:12.30ID:+vRECSeH
>>29 FFIのことを考えつつスライスの恩恵(境界チェックなど)も受けるなら今のRustの文字列に最後に\0を入れるようにしたらいいと思ったけどなんでしないんやろ
\0分の1バイトぐらい今のPCじゃ問題にならないはず
2021/06/21(月) 11:49:33.30ID:hH2X9nxJ
>>32
従来の &str (部分文字列) とnul終端文字列を区別しないといけないけど
型で区別しようとすると結局今のCStr/CStringと同じになるのでは
文字列は部分文字列含め全部Stringみたいにヒープアロケーションするなら良いけどさすがに効率が悪すぎる
2021/06/21(月) 12:03:56.90ID:vy1X2bYf
これps5は60fpsでます??
2021/06/21(月) 12:11:12.63ID:743v8uRC
Rust違いです
ってかゲームのほうのRustって個別スレ無いんだね
2021/06/21(月) 12:12:05.66ID:oYpDc35T
FFIが必要な箇所で
let cstr = std::ffi::CString::new(str);
すれば済む話だからね

Win32APIだと\0終端の2バイト文字も渡したりするからCStringでも使い勝手悪そう
let wcstr: Vec<u16> = std::ffi::CString::new(str).to_str().unwrap().encode_utf16().collect();
で動くかな(試してない)
こっちは自分で'\0'足す方が簡単かもしれない
2021/06/21(月) 12:43:27.64ID:UE5kS0Iw
>>28
気持ちは分かるし、実際、その例に挙がっているケースではそうなんだけど、
字句解析は、コンパイラ理論の状態遷移図に基いて行うと効率が良いが、
それは 0 終端文字列の方が効率が良い。
2021/06/21(月) 12:53:52.20ID:UE5kS0Iw
何度も言うが、全面的にスライスが良いならC言語でも0終端文字列をやめに
してしまえばいいのだが、そういう訳ではない。
>>28 に挙がっているようなケースで、自分も同じような気持ちになったことは有るが、
一方で 0終端文字列の方が効率が良い例も少なからず存在しているので全面的に
スライス方式に変えてしまうのは難しい。
一番単純な例を書けば、英大文字の部分だけを読み飛ばす場合、

(1) ptrが0終端文字列を挿している場合:
while ( *ptr >= 'A' && *ptr <= 'Z' ) ptr++;

(2) (ptr, len)でスライス文字列を表現している場合 :
int cnt = 0;
while ( cnt < len && *ptr >= 'A' && *ptr <= 'Z' ) {ptr++; cnt++;}

後者だと、cnt < len と cnt++; の部分が追加されて効率が落ちる。
2021/06/21(月) 13:02:34.21ID:WbPJLyAM
Linuxカーネル開発における「Rust」採用の動き、グーグルとISRGがさらなる後押し
https://japan.zdnet.com/article/35172646/

> Googleは、ウェブサーバーソフトウェアの「Apache HTTP Server」向けのモジュールをRustによるモジュールで置き換えるというISRGのプロジェクトも支援している。
2021/06/21(月) 13:11:18.52ID:+4cAknZI
windows apiを使ってメッセージダイアログボックスを表示するサンプルが載ってるサイト教えてください
2021/06/21(月) 13:25:05.28ID:hH2X9nxJ
>>38
今のcpuとコンパイラの最適化で両者にどれくらいの性能差があるか示したベンチマークなどある?

rustでも文字列末尾に0を差し込めば同じことはできるので、本当に速くなるなら最適化の手法として採用しても良いかもしれない
2021/06/21(月) 13:41:31.99ID:G7rEBmCP
>>41
試しに手元の環境で1GB分やってみたが1割くらい差があるね
コンパイラはgcc9
でもRustの文字列ってnull文字含むことができるんじゃなかったっけ?試したことないけど
2021/06/21(月) 13:52:57.02ID:xiUkcw19
>>40

use winapi::um::winuser::*;

fn main() {
let str: Vec<u16> = "Hello, world!".encode_utf16().chain(Some(0)).collect();
unsafe{
MessageBoxW(std::ptr::null_mut(), str.as_ptr() , str.as_ptr(), MB_OK);
}
}
2021/06/21(月) 14:00:39.66ID:yyeRDQfZ
設計判断ってのは常にトードオフの選択だからな
ヌル終端にすることで得られるものと失うものを天秤にかける必要がある

得られるものしか見ないやつは設計からは手を引け
2021/06/21(月) 14:01:15.31ID:yyeRDQfZ
トレードオフね
あー恥ずかし
2021/06/21(月) 14:13:19.21ID:t12YpwM9
ああトードオフは重要だよな
2021/06/21(月) 14:31:05.42ID:ILFJsIgR
>>38
ptrとlenが分かってるなら別途カウントアップしていかなくても
どの位置のptrまで読めばいいか最初に分かるんじゃない?
48デフォルトの名無しさん
垢版 |
2021/06/21(月) 14:45:15.77ID:6c6Y8dXA
Rustってなんでprintlnの後にビックリマークあるの?
2021/06/21(月) 14:51:45.48ID:G7rEBmCP
>>47
確かに。end_ptrとの比較で終了判定した場合は(1)と差はなかった
比較一個分くらいなら今どきのプロセッサの並列実行で
十分吸収できるということかな
2021/06/21(月) 14:58:01.41ID:oYpDc35T
>>48
printlnは関数じゃなくマクロだから
ちなみにマクロにしてる理由は引数の型と個数が不定だから
2021/06/21(月) 15:29:27.47ID:+4cAknZI
>>43
先輩ありがとうございます!
2021/06/21(月) 15:40:08.22ID:WbPJLyAM
そういや、可変長引数を直接書けないからRustはクソって言う人はまだ見た事ないな
あんまり使わないからかな?
2021/06/21(月) 16:23:01.35ID:hH2X9nxJ
>>42
文字列中にNULが含まれないことを前提とした最適化だから
NUL含む場合は使えないという制約はあるね
Cの文字列と同じ制約だから実用上あまり困らないんじゃないのかな知らんけど
2021/06/21(月) 16:29:47.09ID:hH2X9nxJ
>>52
Cで可変長引数使いたくなるのってprintf系関数以外なんかある?
55デフォルトの名無しさん
垢版 |
2021/06/21(月) 16:52:01.19ID:xiUkcw19
>>54
ない
標準関数ではprintfとscanfだけ
2021/06/21(月) 17:01:59.90ID:UE5kS0Iw
>>47
なるほど、こういうことかな:
(2)' (ptr, len)でスライス文字列を表現している場合 :
int btm = ptr + len;
while ( ptr < btm && *ptr >= 'A' && *ptr <= 'Z' ) {ptr++;}

>>49
差は少なくはなるが、無くなるわけではない。
ptr < btm という部分が残るから。整数比較命令と 条件jmp命令の
合計2命令はまだ(1)より多い。
2021/06/21(月) 17:23:50.71ID:a6mPBEl9
>>56
そりゃ命令数に差があるのは当然だけど
現代的なプロセッサの並列発行や分岐予測を考慮して
なおパフォーマンス差があるかを見たかっただけなので
そちらは実際測定して差を確認できた?
2021/06/21(月) 17:32:00.25ID:xiUkcw19
議論するのそこ?
むしろセキュリティ(バッファオーバーランの危険性)とパフォーマンスを秤にかけて
rustはセキュリティの方を採用したってだけじゃない?

パスカル文字列なんて昔からあったわけだし
■ このスレッドは過去ログ倉庫に格納されています
5ちゃんねるの広告が気に入らない場合は、こちらをクリックしてください。

ニューススポーツなんでも実況