Rust part8
■ このスレッドは過去ログ倉庫に格納されています
ATS2ってRust以上にドキュメントが少なくてHaskell以上に難解なアレじゃないですかやだー! Rust界隈で一番貢献度高いのってAlexとかになるかな? Microsoftに期待してるんだが今はそれどころじゃ無さそう 勉強がてらこちらの二分探索木を真似してみたのですが、コンパイルエラーが出てしまいます。
https://codereview.stackexchange.com/questions/133209/binary-tree-implementation-in-rust
playgroundで試してみた結果がこれです。
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fc1fe0d2a4c39c6b28d017539419fa2a
どうやら古いRustならコンパイルが通るようなのですが、今のRustでコンパイルを通すにはどうすればよいのでしょうか? 非常に良いと思います。flagの意図を読み解くのが少し難しいくらいです。 確かにflagが読みづらいからコメントは欲しいかな
個人的にはuse Exp::*;とExp::V => Exp::Valueにするのとテストケース増やしてmod testsにするかな
let exp = Value("MITSU").add(Value("MITSU")).add(Value("MITSU")).eql(Value("CORONA"));
let solver = Solver::new(exp);
// prints solved exp
solver.solve();
最初のインスタンスは読みにくいからこうするかも、それかマクロかビルダー作るか トレイトって機能を追加するときどういう機能であるかの特徴を一見して分かりやすくするためだけのもの? >>433
とりあえずはJavaやC#のインターフェースっぽいものとして捉えておけばいいんじゃないのかな
やってるうちに違いも分かってくる思うので これライフタイム記述を省略できないのなんで? https://git.io/JfL6t 省略してコンパイラ通るけどstableは無理なん? 関数内で同じ一連の処理を複数回実行するときマクロにまとめるのが一般的?それともクロージャ?
他にやりかたある?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=30449f979231232b68016cab5cd1fb1e
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e5fc7f66785bb3794df19104d5c479ff >>438
関数
あとサンプルだからかもしれないけど
spilt_whitespace()とかでイテレータ使ったほうが読みやすい https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b03bef5cb6f98821032c5fb1cce618a1 そのコードは実際に使うコードじゃなくてさあ、変数が何個もあったりすると関数切り出しは引数が地獄になるじゃん その例でマクロかクロージャの二択ならクロージャだけど
>>438のは外側の変数をキャプチャすることで複雑化してるから&tangoを渡すようにする
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ec760be4190e2ad4ab21fbea3c84b0ed
そうすると結局クロージャも関数と同じように適切なシグニチャを考える必要があるから
関数化に比べて楽ってことはないんじゃないかと思う 公式のGithubにrustlingsっていう簡単な練習問題がある
The Bookを読んだ後に基本文法をおさらいしたい人向け
https://github.com/rust-lang/rustlings ↓Vecだと動くのに配列にするとコンパイルできないの何で?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6321129e5cc3853ef1421d706e19a136 配列にinto_iterしてもmoveされずに参照が返るから rustいつのまにか蟹のイメージになってるけど、オライリーの影響?それとももっと前から何かあるのかな? let mut heap = BinaryHeap::new();
heap.push(10);
heap.push(9);
heap.push(20);
for x in &heap {
println!("{:?}", x);
}
これでバイナリヒープすると
20
9
10
ってなるんだけどバグ? >>448
配列のIntoIteratorは
&[T; N]と&mut [T; N]に対して実装されてるが[T; N]に対しては実装されてないのでmove不可
対してVecのIntoIteratorは
&Vec<T>と&mut Vec<T>だけでなくVec<T>に対しても実装されてるので
moveが必要なコンテキストならIntoIterator for Vec<T>が選択されmove可能 >>448
配列でやりたい場合はFooにClone実装してfoo.clone()
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8fdb065f764ac7a0be3a3de161bfa984 学術の巨大掲示板群 - アルファ・ラボ
ttp://x0000.net
数学 物理学 化学 生物学 天文学 地理地学
IT 電子 工学 言語学 国語 方言 など
PS 連続と離散を統一した!
ttp://x0000.net/topic.aspx?id=3709-0 配列のIntoIteratorにmoveが実装されてないの何で? >>458
詳しいことはよくわからないけど実装が難しかったみたいよ
興味あればこのissue読んでみて
https://github.com/rust-lang/rust/issues/25725
nightly使えばmoveできる機能はあるみたい
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=6a22ab054f5ec52ea942a1186710a516 >>457
型制約見ればええんやで。
`pub fn iter(&self) -> Iter<T>`は`impl<T> BinaryHeap<T>`。
`impl<T> BinaryHeap<T> where T: Ord,` に`pub fn into_sorted_vec(self) -> Vec<T>`てあるやろ。
rustのOrdは全順序や! この場合はifを使うのとmatchを使うのとどっちがおすすめ?
if nanika_shori().filter(hoshiimonokana).is_some() {
hoge();
mitsukatta();
} else {
fuga();
mitsukaranakatta();
}
match nanika_shori() {
Some(butsu) if hoshiimonokana(butsu) => {
hoge();
mitsukatta();
}
_ => {
fuga();
mitsukaranakatta();
}
} >>461
後者は関数型言語風ってだけでしょ
関数型言語で is_some() にあたる関数は存在しなかったり、有るけど使うなって扱いだったりする テレワーク中なのか連休入ったせいなのか知らんけど猛者が全部回答してくれるから助かるわ >>461
match nanika_shori().filter(hoshiimonokana) {
Some(x) => {…},
None => {…}
}
か
if let Some(x) = nanika_shori().filter(hoshiimononanoka) {…} else {…}
か
boolを返す関数作ってif else
fn are_aruka(hoshiimono: Hoge) -> bool {
match nanika_shori() {
Some(x) if x == hoshiimono => true,
_ => false
}
} filter(...).is_some()
って形はかなり読みにくい
ようは設計が悪い ResultやOptionが複数ネストしたやつをイイカンジにmapとかやれるか Rustやる前に軽くScalaとかHaskellやっとけば
コレクションライブラリ見たときに「あっ、これ進研ゼミでやったところだ!」って
なるから理解が早いよ このコードがコンパイル通って実行できて期待した実行結果にならない
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4472067bedcd681b8154620b3023f278 HashMapでキーを参照にしたときキーがアドレス値にならないのなんで?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9f33505748c782a7b5a2cd22481e5b68 >>473
&strのEqはアドレス比較じゃないから >>471
酷いAPI
stdがどうなってるかぐらい見たほうがいい Rustは、Releaseモードでも、Runtimeをstatic linkする場合は、61MB、
dynamic linkする場合は、8.2MBになるらしい。
dynamic linkで、さらにサイズ重視最適化した場合、4.6MB、strip した場合、
3.1MBになる。
でも、C++に変わる言語を標榜するにしては大きすぎる。
https://www.quora.com/Why-are-the-Rust-and-Go-binaries-so-large-compared-to-C-or-Pascal >>476
>Rustは、Releaseモードでも、Runtimeをstatic linkする場合は、61MB、
>dynamic linkする場合は、8.2MBになるらしい。
Debugモードが61MB
Releaseモード(デフォルト設定)が8.2MB って書いてるよ
それにlinkするのはRuntimeではない
もう少しちゃんと読もう >>476
デフォルトでサイズが大きいという結論は間違ってないけど
もうちょっと英語ちゃんと読んだ方がいいと思う…。
あとその回答には書いてないけど、頑張ればCにサイズで勝つことは可能だったはず。 >>476
Cで書いたプログラムが小さいのは標準ライブラリを利用しているから、だったと思うんだけど違った?
rustはmallocも自身に含めてたはずだし、Cの標準ライブラリ使うならもっと小さくなるはず
って>>476のQuoraの回答にちゃんと書いてあるじゃん!Cで15kb、Rustで14kbって!
自分でリンク貼るならちゃんと読め!もう! release profileでもデバッグシンボル含めてるからprofileオプション安定化までお預けかな。 アイデアありませんか?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=88804aace268a5d6adbdf8a8e4423fe4 scanでどうでしょ
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0092b046fa5b3fa9f1aba90d35a93f4a お前らおっぱいだって大きいほうが好きなくせに何バイナリのファイルサイズが大きめだからって文句言ってるんだよ バイナリサイズの話題ってずーっと昔に出てたよな
ここで出たのかどこで見たのか忘れたけど
Why are Rust executables so huge?
https://stackoverflow.com/questions/29008127
これかなぁ? Noob question: why are Rust binaries so big
https://www.reddit.com/r/rust/comments/9m2dwo/
こっちだったかも let hoge = match s {
"hage" => hage,
"moge"=> moge, // 何かの関数や構造体を返そうとしたがhogeは一意の型しか受け取らないから無理
_=> return
}
loop {
hoge()
}
的な事やろうと色々試したけど、超絶複雑な事になってヤバい匂いしかしない。
ループの外でループ内で呼ぶ関数の分岐を書くのは何かのアンチパターンなのか
そもそも、ループの中でMatch書いても外からイミュータブルな値を渡すの分かってるなら
コンパイラが最適化してループ毎に分岐なんてしないから普通に入れて書けば良い? >>482
inclusiveなtake_untilは標準では無いみたいだから自分で作るかcrate探すかになるっぽい
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b74ec6655adc38e1e790a35485eee01c
https://github.com/rust-lang/rust/issues/62208
既存Traitを拡張するのはめんどくさいから
自分ならイテレータを受け取ってイテレータを返す関数で我慢する >>488
hoge用にhageかmogeをとれるenumを定義すれば型は一意に定まる。
別にループ内でmatchしても分岐予測はほぼ当たるだろうし
性能的に問題はないと思うけど。 あるいはFnとかにして型を揃えれば
loop内の分岐はなくなるかな。
たださっきも書いた通り分岐をなくすことに
それほど意味があるとも思えない。 こういうパターンマッチの使い方ってマイナー?保守性が悪い?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=29945c6422cbc643197b73631691211b >>488
enumで紐つけても結局hogeでmatch使う事になる。
Fnで返すと所謂if文と関数ポインタどっちが早い?的な事になって
最適化するとif文のが早くなる。つまりループの中でMatchしとけば良い。 rustのenumはsum typeだからjitがあればmegamorphicでもtracingじゃなくてもjit効いて
インラインキャッシュも必要ないからそのままインライン展開出来るんだが、この辺がAOTの限界だな。
まあ、分岐予測外れるような呼び出しなら初めからvtable使うだろう。
Rust/WinRT見てc++と違っていきなりマクロ出てくるからMFCの悪夢が蘇った。 >>492
ローカルにあるcrateとrustのソースをgrepしてみたがほとんど使われてないね
シグニチャに書くことで可読性が高まるケースであれば使えばいいと思うけど
そうじゃなければ一旦受けてから関数内で分割したほうがよさそう レンジ自体にマッチしたいんだけど書きかたって今はない?
match 1..3 {
2..3 => println!("2..3"),
1...3 => println!("matched!"),
_ => println!("_"),
} >>496
use std::ops::Range;
match 1..3 {
Range{start: 2, end: 3} => println!("2..3"),
Range{start: 1, end: 3} => println!("matched!"),
_ => println!("_"),
} >>497
ありがとう。
でもなんで
match 1 {
1...2 => println!("matched!"),
_ => todo!(),
}
こっちのマッチは ... で一個点々多く使わないといけなくなったの?
Range{start: 2, end: 3}をつかうなら1...2じゃなくて1..2でもよかったんじゃ ...ってまだ使えるのか。..=に置き換えられたのかと >>498
matchのarmでrangeを使うケースはほぼ100%inclusive rangeなので
half openのexclusive rangeとは区別して利便性を上げつつ
不用意なバグをコンパイル時に弾けるようにしたのが理由だと思うよ
>Range{start: 2, end: 3}をつかうなら1...2じゃなくて1..2でもよかったんじゃ
んん?
そもそも「レンジ自体にマッチしたい」ってどういう用途? Rust の API についてはメソッドのシグネチャとコメントからドキュメントを生成できるけど、
バイナリクレートにユーザー向けのドキュメントを付けるときの作法とかってあるの? i32とかにあるdiv_euclidってどういうときに使うものなの? >>502
「負数の剰余」で調べると違いが分かるかと。 ドキュメントコメントでのassetでのテストとmod testsってどう使い分けたらいいの?
ドキュメントテストすればユニットテスト書かなくていい?
ドキュメントは使用例だけで、assertでのテストって意味なくね?って思ってしまう。無視もできるし >>504
ドキュメントテストはドキュメントであってテストではない。
説明上分かりやすいならassertしてもいいけど、別になくてもいい。
それでもドキュメントテストを通すのは、古いサンプルコードがいつの間にかコンパイル通らなくなっている、というのを防ぐため。
ユニットテストは普通にmod testsで書けばいいよ。 doit()はclone()が入るとエラーが出るんだけど何で?
TにCloneが必要って出て来るけどTの実体をCloneしないのに必要ないだろJKって感じのエラーなんだが?
これどうしたらいいの?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=31635004c1ad9fd9e5ba0f859f28ceb0 >>506
derive使うのやめて自分でClone実装したわ
deriveのCloneがおバカということがわかっただけ収穫とするわ これdoit()のget()がTに'static必要ってコンパイル失敗すんだけど
Tを直接扱ってないんだからTに'static要らんだろって感じなんだが?
関数ポインタくらい静的に解決できんだろJK
どうしたらいいんだよ
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b69fe063a3cc2fae6f1cc7536007fe8e >>505
てことはドキュメントで書いたコードは普通のコンパイルエラー検知しなくてassertのエラーだけ検知するの?
なんかそれはそれでやばくないか コンパイル通さずにどうやってassertだけ通すんじゃ >>508
doitのTにstaticを指定すればいい。fn <T:'static> doit .... >>509
いやいや、コンパイルエラーは検知したくてassertはどっちでもいいって話。
コンパイルできないサンプルコードを検出したいんだから。 なんで型って大文字始まりなのにi32とかは全部小文字始まりになってんの?
初期のデザインミス? primitiveは小文字で統一したんじゃないの? Rustみたいに標準ライブラリは最小に留めてるような言語って他にありますか?
それとコンパイラ内部のCargo.tomlにライブラリのバージョン書けば別に最小に留めなくてもと思うんですが、どうでしょうか? >>516
どこまでを最小というかによるがC++は似たようなもんじゃないかしら これってなんで&mut になんの?
n: mut &i32のほうが差分的にも視覚的にもいいと思うけど、どっかに理由とか書かれてないかな?
fn example(n: &mut i32) {
}
let mut r = &10;
example(mut r); >>519
変数がmutableなのか、immutableなのかと
変数の型がownedなのか、shared referenceなのか、mutable referenceなのかとは別
mutable referenceを表す記号として`&mut`で一塊なのでmut &i32は微妙
n: mut i32みたいに型として意味のないコードを書いて
何が悪いのかわからないってことになりにくい
コンパイラ的にも`&`が先にあったほうが処理が簡単そう ■ このスレッドは過去ログ倉庫に格納されています