公式
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/
探検
Rust part14
■ このスレッドは過去ログ倉庫に格納されています
2022/02/12(土) 01:24:16.59ID:XYE+Rws6
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/
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/
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; }
みたいに変換されると考えれば自然だと思う
前スレ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が呼ばれる
公式の見解とは違うけど変数じゃなくスコープが所有者になるという捉え方のほうが分かりやすければ別にそれでもいいと思う
実装的にはその方が近い
匿名の一時変数(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.
それで正解。
所有権については公式がこう↓うたってんだから、値だとかインスタンスだとかいってる奴は公式に文句言えと。
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の定義で良いと思う
そして学習した後で端折ってある部分が上述の長い説明だと後で理解できれば十分なのではないだろうか
同感
変数に入れないまま関数の戻り値となったり
その関数の戻り値が変数に入れないまま式の中で消費もしくは移動することが多い
そしてdropタイミングはスコープを抜ける時
だから変数よりもスコープが所有権を持っていると考えた方がより近いとの考えに同意する
ただし同じスコープ内で別変数へ移動させた場合もスコープ視点では移動なしとなる
それはdropに関して全く問題ないが変数間の移動を表現しきれていない
一方で属する変数もスコープも移動により次々と移り変わって行く
だから所有権と1対1でリンクしていて不変なものはインスタンスだというのも納得できるしそれ自体は正しく確定
ただし所有権と1対1でリンクしているインスタンス自体も移動していくからその移動を表現しきれていない
したがって
『所有権と1対1でリンクしているのは当然インスタンスとしつつも
そのインスタンスが代入や式や戻り値で使われるたびに変数もしくは見えない一時変数に必ず入っていると考え
さらにその変数が属するスコープの中にインスタンスが毎回イドウすると考え
他へ移動されぬまま属するスコープが消滅するとインスタンス及びその1対1となる所有権も消滅する』
というのが正確なところなのであろう
ただしそれでは学習するには長すぎるから大幅に端折って>>7の定義で良いと思う
そして学習した後で端折ってある部分が上述の長い説明だと後で理解できれば十分なのではないだろうか
2022/02/12(土) 23:33:37.93ID:/iL1/Dd6
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.
と公式で書いてくれてたらスッキリなのにな
オーナー変数がある場合は
* 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
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されそうやね
勉強になりました
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();
もっと楽に記述できるよ、とか、もっと無駄を省けるよ、とか
何でもいいのでアドバイスお願いします
コマンドライン引数の処理を昔ながらのシェルスクリプト風に処理したい場合
こんな感じでいいのでしょうか?
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みたいなオプション引数も扱えなくならない?
手間かかってるだけに見えるし-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();
}
...
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
2022/02/15(火) 22:42:34.06ID:57mqcwZM
2022/02/15(火) 23:44:22.03ID:je481k6i
どちらも文字列の先頭数byte参照するだけだから大差ないよ
as_* じゃなくて to_* を使うとかだと差が出るかもね
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使わなければその点も確実という結論か
あとはみんなでツッコミしてくれ
他にも色々方法がある
例えば落とし穴だが一番表記が短いスライス &(&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でないことと先頭バイトの定数比較コードになると誰もが予測可能
それを満たした上で長さが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
2022/02/17(木) 23:06:37.97ID:S7RVNfva
あらためてRustは凄いな
enum Optionで意味わかりやすく安全にプログラミングしつつ
生成コードからはSomeやNoneが綺麗に跡形もなく消え去ってC言語で書いたときと同じになるんだな
enum Optionで意味わかりやすく安全にプログラミングしつつ
生成コードからはSomeやNoneが綺麗に跡形もなく消え去ってC言語で書いたときと同じになるんだな
2022/02/17(木) 23:36:39.74ID:JQNiobes
ML系言語の概念とC的な低レイヤの概念を融合した上で
オブジェクトの依存性の概念も付け加えるというのは実に素晴らしい考えだと私も思う。
個人的には Haskell が好きなんだけどランタイムサポートが重すぎると思ってたので Rust は大歓迎だ。
オブジェクトの依存性の概念も付け加えるというのは実に素晴らしい考えだと私も思う。
個人的には Haskell が好きなんだけどランタイムサポートが重すぎると思ってたので Rust は大歓迎だ。
2022/02/17(木) 23:47:52.93ID:W9idHeI8
ヒープを使うか使わないかと
dynを使うか使わないかの2点だけは
コンパイラも最適化できないのでそれだけ気をつけていれば大丈夫
dynを使うか使わないかの2点だけは
コンパイラも最適化できないのでそれだけ気をつけていれば大丈夫
2022/02/18(金) 08:09:00.13ID:zucTXNEL
>>32
boxを使わない時は常にスタックじゃないの?
boxを使わない時は常にスタックじゃないの?
2022/02/18(金) 08:29:45.01ID:dvRY56uQ
>>33
BoxだけでなくVecやStringなどもヒープを使う
配列[T;N]や配列を使ったArrayVecやArrayStringなどはスタック上のみ使う
dynはBox<dyn ...>の形ならヒープ上だけど&dynの形ならスタック上のみで使うことができる
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>の形にして返す
例えば標準入力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の形ならスタック上のみで使うことができる
と言われたら、ん?ってならない?
&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>
これはヒープ使うことになる
しかしサイズが不定
つまり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>
これはヒープ使うことになる
これでも
んー・・・ってならないならもういいや
strはヒープを使わない
しかしサイズが不定
つまりstr型の変数はない
スタック上で使う時は&str型の変数に入れて用いる
戻り値にしたいならBox<str>
これはヒープ使うことになる
これでも
んー・・・ってならないならもういいや
2022/02/19(土) 15:49:52.73ID:lVeS0ElI
その置き換えは無理があるんじゃないか
&strは単なる参照で&strの一部でもStringの一部でも代入できる
&dyn Traitはそれを実装する具体型を代入することで生成される
&strは単なる参照で&strの一部でもStringの一部でも代入できる
&dyn Traitはそれを実装する具体型を代入することで生成される
2022/02/19(土) 18:01:00.26ID:yRRevCPm
&dynの参照先はヒープ(例えばBoxの中身)の可能性もあるからヒープを使わないって表現は誤解を招きそう
2022/02/19(土) 18:59:15.30ID:l5YLFJyt
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>とかも使えるからね
&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などの実体を生成している
このように明白に異なる
自己矛盾を起こしているぞ2点も
まず1点目
前者はターゲットstrでimpl Deref for String実装というコードが実際にありその枠組みに従った規定通りの操作
後者はそのような枠組みも実装コードも公開されていない操作
次に2点目
前者はStringの一部を指しているだけであり何か新たな実体を生成してそこを指しているわけではない
後者はFileとは別にvtableなどの実体を生成している
このように明白に異なる
2022/02/20(日) 01:59:50.05ID:Fazun+uY
2022/02/20(日) 02:11:24.76ID:Fazun+uY
>>47
>前者はStringの一部を指しているだけであり何か新たな実体を生成してそこを指しているわけではない
>後者はFileとは別にvtableなどの実体を生成している
ああ、なるほど
trait objectが生成されるときにvtableも生成されると思ってるのか
>前者は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
2022/02/20(日) 10:31:39.82ID:NNOlvVsy
>>50,51
vtableはtrait objectの有無に限らずtrait実装につき1つコンパイル時に作成されそれが共有して使われる
vtableを指すtrait objectも実行時じゃなくコンパイル時に作成される
実行時にはvtable経由のダイナミックディスパッチが発生するだけ
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については生成されない (できない) よね?
そうか、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じゃなければコンパイル時に使われないことがはっきりしてるはずだから作られないと思うけど
trait objectとして使われなくても常にvtableが作成されるかどうかはコンパイラの最適化の話だからここでは関係ないよね
実際のところはdylibじゃなければコンパイル時に使われないことがはっきりしてるはずだから作られないと思うけど
2022/02/20(日) 17:48:45.70ID:Q+YkyZIv
>>55
コンパイル時じゃなくてリンク時の最適化の話じゃないの
オブジェクトファイルには常にvtable埋め込まれてるのかと
それとも dyn Trait を使った側のcrateのオブジェクトファイルにvtableは埋め込まれる?
コンパイル時じゃなくてリンク時の最適化の話じゃないの
オブジェクトファイルには常に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
>> 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が作成される
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は同じアドレスのものになるの?
それとも、異なるものになるの?
同じtraitでも実装された型ごとに違うvtable作らなきゃいけないのは確かに当然か
>>60
異なるcrateからtrait object生成した場合にvtableは同じアドレスのものになるの?
それとも、異なるものになるの?
2022/02/21(月) 23:56:22.93ID:1opwFCgw
>>57
「trait objectを使うようなコードが一切存在しなくても」vtableが作られると言いたかったわけではないんだが書き方が悪かったね
「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
例えば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
2022/02/22(火) 12:24:58.23ID:M+NPwtu0
違う型のvtableがmergeされることもあるみたい
https://rust-lang.github.io/rust-clippy/master/#vtable_address_comparisons
https://rust-lang.github.io/rust-clippy/master/#vtable_address_comparisons
2022/02/22(火) 12:36:19.01ID:Uj7UhjXB
2022/02/22(火) 14:56:23.72ID:S7d5HFwX
>>60
もう少しvtableの情報を詳細にした
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;
という状況で以下が成立
assert_eq!(val!(addr!(dyn_file), 0), addr_file);
let vtable_file = val!(addr!(dyn_file), 1);
assert_eq!(val!(vtable_file, 0), std::ptr::drop_in_place::<std::fs::File> as usize);
assert_eq!(val!(vtable_file, 1), std::mem::size_of::<std::fs::File>());
assert_eq!(val!(vtable_file, 2), std::mem::align_of::<std::fs::File>());
assert_eq!(val!(vtable_file, 3), <std::fs::File as std::io::Read>::read as usize);
assert_eq!(val!(vtable_file, 4), <std::fs::File as std::io::Read>::read_vectored as usize);
つまり型情報が消失したdynにおいてもこのvtableさえ保持していれば正しくトレイトメソッドやデストラクタを呼び出せる
>>65
そのコードだと比較しようとしているのはvtableやそのアドレスではなく
上述コードでのdyn_fileのアドレスを比較しているだけなのでそもそも前提が謎だな
さらにvtableの内容は各型に完全に依存しているからmergeの意味もよくわからない
もう少しvtableの情報を詳細にした
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;
という状況で以下が成立
assert_eq!(val!(addr!(dyn_file), 0), addr_file);
let vtable_file = val!(addr!(dyn_file), 1);
assert_eq!(val!(vtable_file, 0), std::ptr::drop_in_place::<std::fs::File> as usize);
assert_eq!(val!(vtable_file, 1), std::mem::size_of::<std::fs::File>());
assert_eq!(val!(vtable_file, 2), std::mem::align_of::<std::fs::File>());
assert_eq!(val!(vtable_file, 3), <std::fs::File as std::io::Read>::read as usize);
assert_eq!(val!(vtable_file, 4), <std::fs::File as std::io::Read>::read_vectored as usize);
つまり型情報が消失したdynにおいてもこのvtableさえ保持していれば正しくトレイトメソッドやデストラクタを呼び出せる
>>65
そのコードだと比較しようとしているのはvtableやそのアドレスではなく
上述コードでのdyn_fileのアドレスを比較しているだけなのでそもそも前提が謎だな
さらにvtableの内容は各型に完全に依存しているからmergeの意味もよくわからない
2022/02/22(火) 15:27:07.33ID:Uj7UhjXB
空traitなんかは異なる型同士でvtable使い回しできるのでは
あとは全associated fnにデフォルト実装が与えられていて中身が全く同じものとか
いずれにせよLLVMがよしなに最適化してくれるとかなのでは?
あとは全associated fnにデフォルト実装が与えられていて中身が全く同じものとか
いずれにせよLLVMがよしなに最適化してくれるとかなのでは?
2022/02/22(火) 17:06:00.72ID:S7d5HFwX
vtableにはデストラクタも乗ってるようだけどそれも必要ない場合あるもんな
>>67の状況も今後実装が変わったり最適化で消えたり色々ありうるわけだ
いずれにせよ我々は興味本位で語り合ってるだけであり
このあたりの非公開の仕様に依存したコードを書いてはいけないしな
>>67の状況も今後実装が変わったり最適化で消えたり色々ありうるわけだ
いずれにせよ我々は興味本位で語り合ってるだけであり
このあたりの非公開の仕様に依存したコードを書いてはいけないしな
2022/02/22(火) 23:08:55.74ID:uChpKE+n
>>67
>上述コードでのdyn_fileのアドレスを比較しているだけなのでそもそも前提が謎だな
Rc::ptr_eqで単純に比較するとfat pointer内のdataへのpointerとvtableへのpointerの両方が一致してるかのテストになる
>さらにvtableの内容は各型に完全に依存しているからmergeの意味もよくわからない
わかりやすいのは&Tと&mut Tのように可視性のみ違う場合
>上述コードでのdyn_fileのアドレスを比較しているだけなのでそもそも前提が謎だな
Rc::ptr_eqで単純に比較するとfat pointer内のdataへのpointerとvtableへのpointerの両方が一致してるかのテストになる
>さらにvtableの内容は各型に完全に依存しているからmergeの意味もよくわからない
わかりやすいのは&Tと&mut Tのように可視性のみ違う場合
2022/02/22(火) 23:17:56.21ID:WirMN8li
■ このスレッドは過去ログ倉庫に格納されています
ニュース
- 首相官邸前で「戦争あおるな」 台湾有事巡る答弁に抗議 [蚤の市★]
- 高市首相告白「『なめられない服』を選ぶことに数時間を費やしました」「外交交渉でマウント取れる服、買わなくてはいかんかもなぁ」 [ぐれ★]
- 【高市リスク】立民・小西洋之参院議員「高市総理がとんでもない安全保障オンチで外交オンチ」 [ぐれ★]
- 『DOWNTOWN+』会員数50万人突破で見えてきた 松本人志の“月収4ケタ万円”驚愕収入 [阿弥陀ヶ峰★]
- 【音楽】クラフトワークの来日公演決定 [湛然★]
- 【赤坂ライブハウス刺傷】逃走していた自衛官の男(43)を殺人未遂の疑いで逮捕 警視庁 被害女性とは知人関係 [Ailuropoda melanoleuca★]
- 夜勤終わり風呂なう
- 桃香さん!!
- 【悲報】東京都民さん、20過ぎてるのに自転車に乗っててて大炎上wwwwwwwwwwww女「いい歳した男で自転車に乗るのは知的障がい者だけだよ? [483447288]
- 【悲報】細田守最新作、超絶爆死しそう
- 【悲報】ミスター東大さん、高度な『ずらし』を披露するも愚民には理解されず大炎上wwwwwwwwwwww [455031798]
- 俺より芥川龍之介の本読んでるやつVIPにいないであろう
