Rust part14

■ このスレッドは過去ログ倉庫に格納されています
2022/02/12(土) 01:24:16.59ID:XYE+Rws6
公式
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/

※Rustを学ぶ際に犯しがちな12の過ち
https://dystroy.org/blog/how-not-to-learn-rust

※Rustのasyncについて知りたければ「async-book」は必読
https://rust-lang.github.io/async-book/

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

※次スレは原則>>980が立てること

前スレ
Rust part13
https://mevius.5ch.net/test/read.cgi/tech/1636247099/
2022/02/12(土) 01:27:40.45ID:aHobc4uM
Rust The Book (日本語版)
https://doc.rust-jp.rs/book-ja/
Rust edition guide (日本語版)
https://doc.rust-jp.rs/edition-guide/
Rust by example (日本語版)
https://doc.rust-jp.rs/rust-by-example-ja/
Rust cookbook (日本語版)
https://uma0317.github.io/rust-cookbook-ja/
Rust API guideline (日本語版)
https://sinkuu.github.io/api-guidelines/
Rust nomicon book (日本語版)
https://doc.rust-jp.rs/rust-nomicon-ja/
Rust WASM book (日本語版)
https://moshg.github.io/rustwasm-book-ja/
Rust embeded book (日本語版)
https://tomoyuki-nakabayashi.github.io/book/
Rust enbeded discovery (日本語版)
https://tomoyuki-nakabayashi.github.io/discovery/
2022/02/12(土) 01:28:24.19ID:aHobc4uM
Rust CLI (Command Line Interface) apps Book
https://rust-cli.github.io/book/
Rust async-std Book
https://book.async.rs/
Rust The Unstable Book
https://doc.rust-lang.org/nightly/unstable-book/
Rust rustc Book
https://doc.rust-lang.org/rustc/
Rust Cargo Book
https://doc.rust-lang.org/cargo/
The Rust Reference
https://doc.rust-lang.org/reference/
The Rust Standard Library
https://doc.rust-lang.org/std/
2022/02/12(土) 13:48:10.54ID:kNBFVDwU
新スレ乙です

前スレ994流れてたので
(以下引用)
struct S;
impl Drop for S {
fn drop(&mut self) {
println!("drop");
}
}
fn main() {
S;
}
↑じゃあこれは何が所有権をもってて何がdropさせてんの?
インスタンス説のほうがまだシックリくる?
変数も所有権を持てるしスコープ終了で手放せる?
(以上引用)

この場合はmain内の
S;
のところに隠れた一時変数がいて
{ let _s = S; }
みたいに変換されると考えれば自然だと思う
2022/02/12(土) 13:53:07.63ID:qPU2UgbF
前スレ>>994
匿名の一時変数(temporary)が所有者になってenclosing scopeを抜ける時にdropが呼ばれる

公式の見解とは違うけど変数じゃなくスコープが所有者になるという捉え方のほうが分かりやすければ別にそれでもいいと思う
実装的にはその方が近い
2022/02/12(土) 13:54:01.32ID:qPU2UgbF
すまん、モロ被りした
7デフォルトの名無しさん
垢版 |
2022/02/12(土) 16:30:44.26ID:v8ccrYYP
>>4 >>5
それで正解。

所有権については公式がこう↓うたってんだから、値だとかインスタンスだとかいってる奴は公式に文句言えと。

4.1. What is ownership?
(ttps://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 a variable that’s called its owner.
* There can only be one owner at a time.
* When the owner goes out of scope, the value will be dropped.
8デフォルトの名無しさん
垢版 |
2022/02/12(土) 16:59:30.70ID:v8ccrYYP
非同期のデファクトライブラリはtokioかと思うがasync-std使ってる人おる?
2022/02/12(土) 17:24:19.71ID:LO6bM52V
sqlxがasync-stdのサポート切るって聞いたけどほんと?
2022/02/12(土) 23:26:08.10ID:/iL1/Dd6
>>5
同感
変数に入れないまま関数の戻り値となったり
その関数の戻り値が変数に入れないまま式の中で消費もしくは移動することが多い
そしてdropタイミングはスコープを抜ける時
だから変数よりもスコープが所有権を持っていると考えた方がより近いとの考えに同意する
ただし同じスコープ内で別変数へ移動させた場合もスコープ視点では移動なしとなる
それはdropに関して全く問題ないが変数間の移動を表現しきれていない

一方で属する変数もスコープも移動により次々と移り変わって行く
だから所有権と1対1でリンクしていて不変なものはインスタンスだというのも納得できるしそれ自体は正しく確定
ただし所有権と1対1でリンクしているインスタンス自体も移動していくからその移動を表現しきれていない

したがって
『所有権と1対1でリンクしているのは当然インスタンスとしつつも
そのインスタンスが代入や式や戻り値で使われるたびに変数もしくは見えない一時変数に必ず入っていると考え
さらにその変数が属するスコープの中にインスタンスが毎回イドウすると考え
他へ移動されぬまま属するスコープが消滅するとインスタンス及びその1対1となる所有権も消滅する』
というのが正確なところなのであろう

ただしそれでは学習するには長すぎるから大幅に端折って>>7の定義で良いと思う
そして学習した後で端折ってある部分が上述の長い説明だと後で理解できれば十分なのではないだろうか
2022/02/12(土) 23:33:37.93ID:/iL1/Dd6
>>8
使っている

>>9
その議論を少し読んでみたけど
サポート続けるべきの声もある程度あるし
何らか用途ごとにもう少し抽象化したインタフェースを確立して両立利用できるようにすべき声もあるようだ
明るい未来になるとうれしい
2022/02/13(日) 09:35:24.39ID:ykPYhkFQ
見えない一時変数とか想像しなくても
オーナー変数がある場合は
* There can only be one owner at a time.
* When the owner goes out of scope, the value will be dropped.
で、無い場合は
* When the value ほにゃららタイミング, the value will be dropped.
と公式で書いてくれてたらスッキリなのにな
2022/02/13(日) 11:34:54.98ID:NHDjNJ0w
>>12
一時変数は想像上の産物ではないよ
リファレンスに変数の一つとして定義されてるし
どういう時に一時変数が作られるのかも定義されてる
The Bookに詳しく書いてないだけ
2022/02/13(日) 11:56:22.68ID:ykPYhkFQ
>>13
勉強になりました

https://doc.rust-lang.org/reference/variables.html
> A variable is a component of a stack frame, either a named function parameter,
> an anonymous temporary, or a named local variable.

https://doc.rust-lang.org/reference/expressions.html#temporaries
> When using a value expression in most place expression contexts,
> a temporary unnamed memory location is created and initialized to that value.
> The expression evaluates to that location instead, except if promoted to a static.
> The drop scope of the temporary is usually the end of the enclosing statement.

匿名一時変数は式の終わりでdropされそうやね
2022/02/13(日) 12:07:59.97ID:ykPYhkFQ
おっと式じゃなくて文の間違い
2022/02/14(月) 21:08:19.96ID:JD6MLhyb
外部ライブラリを使わずに
コマンドライン引数の処理を昔ながらのシェルスクリプト風に処理したい場合
こんな感じでいいのでしょうか?
let mut args = std::env::args().peekable();
let cmd_name = args.next().unwrap();
let usage = || {
eprintln!("Usage: {cmd_name} [-d] [-q] [--] <target>");
std::process::exit(1);
};
let mut is_debug = false;
let mut is_quiet = false;
while let Some(b'-') = args.peek().and_then(|arg| arg.as_bytes().first()) {
let option = args.next().unwrap();
match option.as_str() {
"-d" => is_debug = true,
"-q" => is_quiet = true,
"--" => break,
_ => usage(),
}
}
if args.len() != 1 {
usage();
}
let target = args.next().unwrap();
もっと楽に記述できるよ、とか、もっと無駄を省けるよ、とか
何でもいいのでアドバイスお願いします
2022/02/15(火) 00:56:03.94ID:qT41k7yT
peekableにする意味ある?
手間かかってるだけに見えるし-o output.txtみたいなオプション引数も扱えなくならない?
2022/02/15(火) 16:20:56.03ID:yLJ5RfL8
look aheadのパターンにしたかったのかな
Peekableならnext_ifがいい感じにはまりそう

...
let mut is_debug = false;
let mut is_quiet = false;
while let Some(option) = args.next_if(|s| s.starts_with("-")) {
match option.as_str() {
"-d" => is_debug = true,
"-q" => is_quiet = true,
"--" => break,
_ => usage(),
}
}
if args.len() != 1 {
usage();
}
...
2022/02/15(火) 22:18:31.12ID:57mqcwZM
>>17
そういうのは >>16にこれ追加で行けそう
"-o" => output = args.next().or_else(|| { usage(); None }),
2022/02/15(火) 22:42:34.06ID:57mqcwZM
ところで
>>16の as_bytes().first() と
>>18の s.starts_with("-") はどっちが速いのかな?
今回は誤差として例えば数万行のテキストで#から始まるコメント行を除外したい場合
最善手は何?
2022/02/15(火) 23:44:22.03ID:je481k6i
どちらも文字列の先頭数byte参照するだけだから大差ないよ
as_* じゃなくて to_* を使うとかだと差が出るかもね
2022/02/16(水) 01:25:58.91ID:3jEsfDTa
短くてもいつもの汚コード氏だと分かるコードなのがすごい
2022/02/16(水) 18:46:05.60ID:4BNkCNLv
>>20
他にも色々方法がある
例えば落とし穴だが一番表記が短いスライス &(&s)[..1]
ただし空行をs.len()で回避した場合でも
if "#" == &(&s)[..1] { これは先頭が非ASCIIだとpanicなので注意
utf8境界を見極めて最初の1文字を取り出すには面倒で
if "#" == &(&s)[..s.char_indices().nth(1).unwrap().0] { となり本末転倒
なので最初の1文字を取り出すには普通はイテレータchars()を使って
if Some('#') == s.chars().next() { となるが
今回は正しく先頭1文字ではなく先頭1バイトを取り出せれば判断できるので
if Some(b'#') == s.bytes().next() { の方が速い
しかしイテレータを使うまでもないから
if Some(&b'#') == s.as_bytes().get(0) { と配列アクセスでも十分だろう
先頭は需要が大きいためなのか
if Some(&b'#') == s.as_bytes().first() { と専用メソッドが用意されてるようだ
ただしここでもサボって
if b'#' == s.as_bytes()[0] { とすると空行でpanicするので注意
以上ここまでは先頭取り出し系だがシンプルに
if s.starts_with("#") { が一番わかりやすい
ただしこれは実装でinline指定がないから不利で遅いかもしれない
inlineで&[u8]に読み替えるだけの as_bytes() は確実に速い
あとは get(0) と first() がどちらもinlineでコードが
前者が if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None }
後者が if let [first, ..] = self { Some(first) } else { None }
たぶん展開して最適化した最終コードは同じになるのではないか
そしてSomeやNoneは最適化で if Some(&b'#') == 部分と反応しておそらく消えると予想しているが
if let [b'#', ..] = s.as_bytes() { と最初からOption使わなければその点も確実という結論か
あとはみんなでツッコミしてくれ
2022/02/17(木) 10:37:24.58ID:nN2LTJ+a
premature optimizationの典型例
このレベルの最適化が本当に必要ならまずは計測しろ
2022/02/17(木) 10:41:11.69ID:J5He3Z4Q
百理ある
2022/02/17(木) 11:01:53.16ID:W9idHeI8
結論の if let [b'#', ..] = s.as_bytes() { なら可読性も落ちず意味もわかりやすいからいいんじゃね?
それを満たした上で長さが0でないことと先頭バイトの定数比較コードになると誰もが予測可能
2022/02/17(木) 11:06:38.82ID:reqFguXW
うーんRustのデフォルトがムーブになっているのはおもしろいな
かなり思い切った設計をしている
2022/02/17(木) 11:42:39.48ID:RuZDOzbq
千里ある
2022/02/17(木) 12:41:58.72ID:TvxpbZK8
>>20
リテラルなら差はないみたいね
https://godbolt.org/z/hGrPEa5rc
2022/02/17(木) 23:06:37.97ID:S7RVNfva
あらためてRustは凄いな
enum Optionで意味わかりやすく安全にプログラミングしつつ
生成コードからはSomeやNoneが綺麗に跡形もなく消え去ってC言語で書いたときと同じになるんだな
2022/02/17(木) 23:36:39.74ID:JQNiobes
ML系言語の概念とC的な低レイヤの概念を融合した上で
オブジェクトの依存性の概念も付け加えるというのは実に素晴らしい考えだと私も思う。
個人的には Haskell が好きなんだけどランタイムサポートが重すぎると思ってたので Rust は大歓迎だ。
2022/02/17(木) 23:47:52.93ID:W9idHeI8
ヒープを使うか使わないかと
dynを使うか使わないかの2点だけは
コンパイラも最適化できないのでそれだけ気をつけていれば大丈夫
2022/02/18(金) 08:09:00.13ID:zucTXNEL
>>32
boxを使わない時は常にスタックじゃないの?
2022/02/18(金) 08:29:45.01ID:dvRY56uQ
>>33
BoxだけでなくVecやStringなどもヒープを使う
配列[T;N]や配列を使ったArrayVecやArrayStringなどはスタック上のみ使う
dynはBox<dyn ...>の形ならヒープ上だけど&dynの形ならスタック上のみで使うことができる
2022/02/18(金) 09:51:02.85ID:5+RbHFST
>&dynの形ならスタック上のみで使うことができる
ん?
2022/02/18(金) 10:17:03.62ID:dvRY56uQ
>>35
例えば標準入力stdin()とファイルFile::open()は型がStdinとFileで異なるが
どちらもtrait Readを実装しているから&mut dyn Readに入れることで同じコードでreadできるようになる
このdyn Traitをtrait objectと言って動的ディスパッチとなるが生成コードは一つになる
ちなみにimpl Traitだとコンパイル時に静的に解決されて生成コードは型毎に多数のコードが生成される

trait objectは?Sizedだから直接は扱わずに&dynの形かBox<dyn>の形で使うことになる
&dynの形ならスタック上のみで使うことができるが元の型よりは長く生きられない制限がある
そこで例えば関数の返り値にしたい時などはヒープを使ってBox<dyn>の形にして返す
2022/02/18(金) 18:16:26.39ID:XJ022fQi
Box<T>の形ならヒープ上だけど
&Tの形ならスタック上のみで使うことができる

と言われたら、ん?ってならない?
2022/02/18(金) 18:18:33.56ID:QcWsB3dM
ヒープ使わないんだったら、そりゃスタックになるが、
そもそもなにか勘違いしてない?
2022/02/18(金) 19:03:20.48ID:7ZNuwX6P
&dyn の形ならスタックで "も" 使うことができる
ってこと?
2022/02/19(土) 02:35:40.94ID:l5YLFJyt
dynはヒープを使わない
しかしサイズが不定
つまりdyn型の変数はない
スタック上で使う時は&dyn型の変数に入れて用いる
戻り値にしたいならBox<dyn Trait>
これはヒープ使うことになる
2022/02/19(土) 03:42:10.99ID:ukdLXHKY
(stable rustでは) dyn 型の変数はない
が正しい
2022/02/19(土) 13:30:36.11ID:Pa/XmNxa
>>40
strはヒープを使わない
しかしサイズが不定
つまりstr型の変数はない
スタック上で使う時は&str型の変数に入れて用いる
戻り値にしたいならBox<str>
これはヒープ使うことになる


これでも
んー・・・ってならないならもういいや
2022/02/19(土) 15:49:52.73ID:lVeS0ElI
その置き換えは無理があるんじゃないか
&strは単なる参照で&strの一部でもStringの一部でも代入できる
&dyn Traitはそれを実装する具体型を代入することで生成される
2022/02/19(土) 18:01:00.26ID:yRRevCPm
&dynの参照先はヒープ(例えばBoxの中身)の可能性もあるからヒープを使わないって表現は誤解を招きそう
2022/02/19(土) 18:59:15.30ID:l5YLFJyt
>>41
stable前提話でごめん
unsized localがstableとなるには
一旦可変長配列を切り離さないと厳しい?

>>44
そんなことを書いてる人はいない
俺が書いた分も、dyn自体は(arrayなどと同様に)ヒープを使わない、だけだぞ
しかもその後にBoxでヒープを使う話もしている
2022/02/19(土) 22:32:05.29ID:uI4ynUv+
>>43
&strがstrへの参照であるように
&dyn Traitもdyn Traitへ単なる参照
&strにStringを代入すればDerefがstrを生成してるように
&dyn ReadにFileを代入すればコンパイラがdyn Readを生成してる

BoxだけでなくRc<dyn Trait>とかも使えるからね
2022/02/19(土) 23:18:16.06ID:lVeS0ElI
>>46
自己矛盾を起こしているぞ2点も
まず1点目
前者はターゲットstrでimpl Deref for String実装というコードが実際にありその枠組みに従った規定通りの操作
後者はそのような枠組みも実装コードも公開されていない操作
次に2点目
前者はStringの一部を指しているだけであり何か新たな実体を生成してそこを指しているわけではない
後者はFileとは別にvtableなどの実体を生成している
このように明白に異なる
2022/02/20(日) 01:59:50.05ID:Fazun+uY
>>47
strとtrait objectという別のものなんだから違いがあるのは当たり前
>>40>>42の対比が成立しないと言えるような違いを指摘しないと意味ないよ
2022/02/20(日) 02:11:24.76ID:Fazun+uY
>>47
>前者はStringの一部を指しているだけであり何か新たな実体を生成してそこを指しているわけではない
>後者はFileとは別にvtableなどの実体を生成している

ああ、なるほど
trait objectが生成されるときにvtableも生成されると思ってるのか
2022/02/20(日) 02:49:14.40ID:Q+YkyZIv
dynにキャストするときにコンパイラがfat pointerから参照させる用のvtableを生成することを指しているのでは?
2022/02/20(日) 03:20:02.69ID:Y4d5gioW
>>48
そのように大きく違いのある全く別のものを対比させるのは無意味なので、置き換えは無理があるんじゃないか、と書いた

>>49
そこは実行時にvtableを生成とも誤解しかねない書き方をしてすまん
vtable自体は型とtraitのペアで静的に確定するものでありコンパイル時に生成している
2022/02/20(日) 10:31:39.82ID:NNOlvVsy
>>50,51
vtableはtrait objectの有無に限らずtrait実装につき1つコンパイル時に作成されそれが共有して使われる

vtableを指すtrait objectも実行時じゃなくコンパイル時に作成される
実行時にはvtable経由のダイナミックディスパッチが発生するだけ
2022/02/20(日) 11:23:51.17ID:Q+YkyZIv
>>52
そうか、crate内で静的ディスパッチしかしてなくても他crateからtrait objectとして使われる場合もあるから常にvtableは生成されるのね

ちなみに、object safeでないtraitについては生成されない (できない) よね?
2022/02/20(日) 13:54:46.64ID:xQDNRCh/
Rustって関数のデフォルト引数ないの?
2022/02/20(日) 14:48:11.68ID:MpUmJclU
>>53
trait objectとして使われなくても常にvtableが作成されるかどうかはコンパイラの最適化の話だからここでは関係ないよね
実際のところはdylibじゃなければコンパイル時に使われないことがはっきりしてるはずだから作られないと思うけど
2022/02/20(日) 17:48:45.70ID:Q+YkyZIv
>>55
コンパイル時じゃなくてリンク時の最適化の話じゃないの
オブジェクトファイルには常にvtable埋め込まれてるのかと
それとも dyn Trait を使った側のcrateのオブジェクトファイルにvtableは埋め込まれる?
2022/02/20(日) 22:19:41.32ID:uSEnVnLU
>>51
>> vtable自体は型とtraitのペアで静的に確定するものでありコンパイル時に生成

>>52
>> vtableはtrait objectの有無に限らずtrait実装につき1つコンパイル時に作成

この件だがコンパイラのソースを見ると
必要となる型の分だけコンパイル時にvtableを作成してるよな
https://github.com/rust-lang/rust/blob/stable/compiler/rustc_codegen_ssa/src/meth.rs#L53
2022/02/21(月) 00:37:14.94ID:Vy+crfrM
crateあたり同一traitのvtableは最大一つ生成されるということかな
2022/02/21(月) 00:58:29.74ID:hWQuQvUr
同一traitが複数の型で実装されてたらその型ごとに作られるはず
60デフォルトの名無しさん
垢版 |
2022/02/21(月) 17:43:56.41ID:LC1rF3os
各型毎にtrait objectやvtableはこういう構造になっている
let mut stdin: std::io::Stdin = std::io::stdin();
let addr_stdin = addr!(stdin); // &mut借用する前にアドレスを得ておく
let dyn_stdin: &mut dyn std::io::Read = &mut stdin; // ここで dyn Readのtrait object作成
assert_eq!(val!(addr!(dyn_stdin), 0), addr_stdin); // 前半は元のstdinを指している
// assert_eq!(val!(addr!(dyn_stdin), 1), 【Stdin用のvtable】); // 後半はStdin用vtableをを指している
assert_eq!(val!(val!(addr!(dyn_stdin), 1), 3), <std::io::Stdin as std::io::Read>::read as usize);
assert_eq!(val!(val!(addr!(dyn_stdin), 1), 4), <std::io::Stdin as std::io::Read>::read_vectored as usize);

別の型でも同じようにdyn Read作成
let mut file: std::fs::File = std::fs::File::open("/dev/null").unwrap();
let addr_file = addr!(file);
let dyn_file: &mut dyn std::io::Read = &mut file; // trait object作成
assert_eq!(val!(addr!(dyn_file), 0), addr_file); // 前半は元のfileを指している
// assert_eq!(val!(addr!(dyn_stdin), 1), 【File用のvtable】); // 後半はFile用vtableをを指している
assert_eq!(val!(val!(addr!(dyn_file), 1), 3), <std::fs::File as std::io::Read>::read as usize);
assert_eq!(val!(val!(addr!(dyn_file), 1), 4), <std::fs::File as std::io::Read>::read_vectored as usize);
上述Stdinの時と同じ位置にFile用のread()やread_vectored()がvtableに入っていることがわかる

したがって>>59>>51が正しいと思う
各trait毎にvtableのメソッドの位置が決まり
そのtraitを実装する各型毎にvtableが作成される
2022/02/21(月) 18:18:27.01ID:Vy+crfrM
traitをimplした型が違ったら対応するfnの中身も違うので
同じtraitでも実装された型ごとに違うvtable作らなきゃいけないのは確かに当然か

>>60
異なるcrateからtrait object生成した場合にvtableは同じアドレスのものになるの?
それとも、異なるものになるの?
2022/02/21(月) 23:56:22.93ID:1opwFCgw
>>57
「trait objectを使うようなコードが一切存在しなくても」vtableが作られると言いたかったわけではないんだが書き方が悪かったね
2022/02/22(火) 00:04:11.51ID:DlNWIu8c
vtable自体はデバッグビルドのasmやllvm-irで確認できるよ
例えばxという名前のメソッド1つだけ持つFooトレイトのi32実装ならこんな形でvtableが出力される

.quad core::ptr::drop_in_place<i32>
.asciz "¥004¥000¥000¥000¥000¥000¥000¥000¥004¥000¥000¥000¥000¥000¥000"
.quad <i32 as playground::Foo>::x
2022/02/22(火) 11:55:50.79ID:M+NPwtu0
>>61
crateごとじゃなくcodegen unitごとにvtableが作成される可能性が一応ある
crateが違っても必ずしも複数のvtableができるわけではない
2022/02/22(火) 12:24:58.23ID:M+NPwtu0
違う型のvtableがmergeされることもあるみたい
https://rust-lang.github.io/rust-clippy/master/#vtable_address_comparisons
■ このスレッドは過去ログ倉庫に格納されています
5ちゃんねるの広告が気に入らない場合は、こちらをクリックしてください。

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