21/11/07 10:05:36.56 pJhT3MIE.net
Rust The Book (日本語版)
URLリンク(doc.rust-jp.rs)
Rust edition guide (日本語版)
URLリンク(doc.rust-jp.rs)
Rust by example (日本語版)
URLリンク(doc.rust-jp.rs)
Rust cookbook (日本語版)
URLリンク(uma0317.github.io)
Rust API guideline (日本語版)
URLリンク(sinkuu.github.io)
Rust nomicon book (日本語版)
URLリンク(doc.rust-jp.rs)
Rust WASM book (日本語版)
URLリンク(moshg.github.io)
Rust embeded book (日本語版)
URLリンク(tomoyuki-nakabayashi.github.io)
Rust enbeded discovery (日本語版)
URLリンク(tomoyuki-nakabayashi.github.io)
3:デフォルトの名無しさん
21/11/07 10:06:41.30 pJhT3MIE.net
Rust CLI (Command Line Interface) apps Book
URLリンク(rust-cli.github.io)
Rust async-std Book
URLリンク(book.async.rs)
Rust The Unstable Book
URLリンク(doc.rust-lang.org)
Rust rustc Book
URLリンク(doc.rust-lang.org)
Rust Cargo Book
URLリンク(doc.rust-lang.org)
The Rust Reference
URLリンク(doc.rust-lang.org)
The Rust Standard Library
URLリンク(doc.rust-lang.org)
4:デフォルトの名無しさん
21/11/07 12:23:40.47 yCoK9XU3.net
乙
シャドーイングが困るのって関数がよっぽど長いときだけなんで問題にならんよね
5:デフォルトの名無しさん
21/11/07 13:02:12.62 BjoZRpKT.net
>前スレの質問「間違って同じ変数名つけたコード片突っ込んでしまった系だと他言語にあるらしい2重宣言エラーないの怖くないですか?」
他の言語でもスコープが違えば二重宣言エラーになりません
つまり他の言語でも意図しない同名変数ミスは同じように起き得ます
例えば以下はC言語の例
int c = 100;
int main() {
int c = 200;
{
int c = 300;
}
printf("%d\n", c); /* コンパイルは通って 200 となる */
}
Rustのシャドーイングは同じブロックスコープでも同名変数が通りますが
仮にミスで同名変数を付けたコードを挿入してしまったとしても
・その時点で先行の変数が既にライフタイム尽きていれば影響ゼロ
・その時点で先行の変数のライフタイムがあって割り込む形になる場合
・それ以降で二度の消費が発生して借用エラー発生となる可能性が高い
・先行の変数が一度も使われていない段階なら未使用warning発生
・型が違っていれば割り込む形であっても型違いでエラー発生
と影響ゼロもしくは引っかかる可能性が高いでしょう
>>4
おっしゃる通り、そもそも意図しない同名変数に気付かない時点で「その関数は長すぎ」ですね
6:デフォルトの名無しさん
21/11/07 13:03:26.24 GwJCd0Qm.net
ある値をちょっとだけ処理したものにどんな名前を付けたいかってのは、
まあ適した名前があるならそれに越したことは無いけど
同じ名前を付けることによってたいした違いはない、かつ以前の値にアクセスすることはもうないことが明示できるので
それはそれで使いようはあるよ。
以前の値にアクセスしないという意思を持ってシャドーイングを使うのでそれで困ることはない。
うっかりで同じ名前を付けてしまった場合も大抵の場合は型の違いとかで検出されるし。
7:デフォルトの名無しさん
21/11/07 14:26:48.84 c7aT0NV0.net
何を問題にしてるのか分からんけど、コンパイラから見たら、同じ変数名宣言でも連番で構文解析しているわけで
ブロックスコープによりシャドーされても何ら関係ないが、インライン展開して最適化するrustコードだと問題が
出る場合もありうる。それとシャドーと以前の値にアクセスすることはもうない事は意味が違う
8:デフォルトの名無しさん
21/11/07 14:35:43.57 GwJCd0Qm.net
>>7
間接的にアクセスしうるとかいうのはもちろんあるけど、
あくまでもプログラマが読み書きする上での意図として明示するという意味ね。
実際にもう (直接には) 使えないんだから使えないという意味だよ。
9:デフォルトの名無しさん
21/11/07 14:45:10.83 K5DEbKWG.net
ん?まあありうるだろうけど、そんな意図を持ってシャドーイングをするコードは捨てろよ?明示じゃねーわ
10:デフォルトの名無しさん
21/11/07 15:07:42.51 GwJCd0Qm.net
そうか。
11:デフォルトの名無しさん
21/11/07 15:10:45.19 XJB+ymj6.net
>>996
C/C++だとスコープ中で外のスコープの宣言と被ってたら警告出るよね
12:デフォルトの名無しさん
21/11/07 15:27:52.97 BjoZRpKT.net
>>7
>それとシャドーと以前の値にアクセスすることはもうない事は意味が違う
それはシャドーされてもスコープ終了と異なり、尽きてなければライフライムは残ってるという話だよね?
例えば
let mut a = vec![1,2,3];
let b = &mut a;
let a = [4,5,6];
b.extend(a);
assert_eq!(b, &[1, 2, 3, 4, 5, 6]);
>>11
被ってるCのコード>>5を今gcc test.cしたけどその警告出ない
13:デフォルトの名無しさん
21/11/07 16:09:12.89 XJB+ymj6.net
おま環
14:デフォルトの名無しさん
21/11/07 16:58:36.83 NoqiBvXu.net
intellijでも、シャドーイングがあったときは警告出てなかったっけ?
15:デフォルトの名無しさん
21/11/07 17:10:25.95 qLFsTeDp.net
変数名にシャドーイングって含めとけばいいのでは
16:デフォルトの名無しさん
21/11/07 17:23:30.13 BGBI+61D.net
そんなことよりもだ!Rustに限らないけど
これって誰も食いつかないんだけど、1と2どっちが良いか、そろそろ決着つけてくれ
let upper = 1000;
// 1、これと
let mut acc = 0;
for n in 0.. {
let n_squared = n * n;
if n_squared >= upper {
break;
} else if n_squared % 2 == 1 {
acc += n_squared;
}
}
// 2、これ
let acc1: u32 = (0..).map(|n| n * n)
.take_while(|&n_squared| n_squared < upper)
.filter(|&n_squared| is_odd(n_squared))
.fold(0, |acc, n_squared| acc + n_squared);
17:デフォルトの名無しさん
21/11/07 17:27:45.44 Vt90/0HU.net
foldじゃなくてsumのほうが良いのでは
18:デフォルトの名無しさん
21/11/07 17:29:32.83 TOVMzjUd.net
let acc1: u32 = (0..).map(|n| n * n)
.take_while(|&n_squared| n_squared < upper)
.filter(is_odd)
.sum()
19:991
21/11/07 17:45:31.24 tLg/y1Lc.net
>>5
不意のミスでも結構コンパイルで引っかかってくれるようで安心しました。Rustさんよく考えられてますね。知りたかったことが書いてありました。ご丁寧にありがとうございます
20:デフォルトの名無しさん
21/11/07 18:44:40.25 jMCdC4Py.net
shadowing嫌いな人はdeny(clippy::shadow_same)などすればよい
21:デフォルトの名無しさん
21/11/07 18:52:44.84 UxYGnxuj.net
>>16
High orders functionはソースが配列かイテレーターか、いずれかで注意が必要です。配列の場合は以下の
英文のようになります。つまりそのサイズのメモリー領域が必要になるということです。またイテレータの
時でもある程度メモリーは当然使用しますが、それよりも遅延評価されるので注意が必要です。
Note on performance and stack usage
Unfortunately, usages of this method are currently not always optimized as well as they could be.
This mainly concerns large arrays, as mapping over small arrays seem to be optimized just fine.
Also note that in debug mode (i.e. without any optimizations), this method can use a lot of stack space
(a few times the size of the array or more).
Therefore, in performance-critical code, try to avoid using this method on large arrays or check
the emitted code. Also try to avoid chained maps (e.g. arr.map(...).map(...)).
個人的には深いチェーン呼び出しはあまり好きではありません。なぜなら状態をデバックしにくいからです。
パフォーマンス的な罠がありデバックしにくい事を抜けば、map,take_while,filterなどは何を行うかforに
比べ意図が明確になりますが、それは自己満足の幅が大きいとも言えます
22:デフォルトの名無しさん
21/11/07 19:20:41.26 AmV/cRIg.net
>>18のコードは大きな配列とかメモリ大量消費とかしないだろ
小さなstructを以下の4つ消費するのみで誤差
RangeFrom, Map, TakeWhile, Filter
23:デフォルトの名無しさん
21/11/07 19:35:16.03 UxYGnxuj.net
>>22
18のコードは配列ではなくイテレーターなのでそうですが、map,take_while,filterのほうが
好みの人が多いとは思いますが、私が言いたいのは何でもかんでもHigh orders functionに
して書くのはよく知らないと、リスクがあるということとブレークポイントなどを仕掛けて
経過は見れないということです。日本語が読めない人はもう少し考えましょう
24:デフォルトの名無しさん
21/11/07 19:40:56.86 BxmvbDqp.net
メソッドチェーンなら、メソッドごとに単体テストができるし、テストを用意しとけばすぐにバグがわかるような気がするが。
25:デフォルトの名無しさん
21/11/07 21:07:08.18 UxYGnxuj.net
ほんと日本語読めない奴ばっかり。経過いうてるのにメソッド毎だとか、バグのことなんて言って
ないのに(特殊な例を言えば配列の場合でmap().map()などとしたメモリー使用量)を言っている
のに、そもそも個人的には言うてんのに、そんなに説得したいのか?
26:デフォルトの名無しさん
21/11/07 21:12:13.09 0IMrdMn2.net
翻訳は、Chrome, Edge の翻訳機能、
Google の翻訳サイトとか、DeepL とか
27:デフォルトの名無しさん
21/11/07 21:12:13.85 t/1xwUA+.net
経過も見れるよ
28:デフォルトの名無しさん
21/11/07 21:20:10.03 UxYGnxuj.net
言語は素晴らしいのにやってる奴が意識高い系のウンコ野郎ばっかり
29:デフォルトの名無しさん
21/11/07 21:27:42.49 aMDc0Kvs.net
まあコミュニティに馴染めるかどうかも本人の適性によるところがあるしな
30:デフォルトの名無しさん
21/11/07 22:21:32.27 BjoZRpKT.net
>>25
前回から新たに導入されたfn map([T; N], FnMut(T) -> U) -> [U; N]を何度も使えば配列地獄になるけど
昔からあるIterator::map()を使えば小さな struct Mapの分しか容量を喰わないよ
前者: assert_eq!(10, [1, 2, 3, 4, 5].map(|n| n - 3).map(|n| n * n).into_iter().sum());
後者: assert_eq!(10, [1, 2, 3, 4, 5].into_iter().map(|n| n - 3).map(|n| n * n).sum());
したがってこの件でメソッドチェーンを批判するのはおかしい
そして元々の>>16課題forを使うか>>18のメソッドチェーンを使うかの話に配列は一切関係ない
31:デフォルトの名無しさん
21/11/07 22:27:15.28 jMCdC4Py.net
配列とスライスとベクタが別物なのこういう場所で会話するときに地味に混乱しがち
32:デフォルトの名無しさん
21/11/07 22:28:05.16 jMCdC4Py.net
Stringとstrって日本語でどう呼び分けるんですか?
33:デフォルトの名無しさん
21/11/08 07:17:46.35 59xs5iEI.net
inspectで経過見れるじゃん
34:デフォルトの名無しさん
21/11/08 07:29:23.24 n6XCACTA.net
>>30
こういう内容ゼロの事を言ってる意識高い系が普及を邪魔してる。自身が”何度も使えば配列地獄になるけど”と
認めてるにも関わらず、最初からイテレータを使えばと先に言ってることをそのまま返す
何が”したがって”なのか全然分からんが、説明した気になり、�
35:hチェーンを使うかの話に配列は関係ない”と まとめる。そもそもチェーンを批判なんてしてない(対象によりけり高階関数の連続使用はメモリーを圧迫する 可能性があるということだけ)それとも言い負かしたいだけの気持ち悪さをそんなに使用者に伝えたいのか?
36:デフォルトの名無しさん
21/11/08 07:59:01.69 SITQ70se.net
>>25
君が二つの全く異なるmap()をゴチャ混ぜにして皆を騙して混乱させているだけだ
我々はfor文で回すかイテレータをチェーンするかの話をしている
君が持ち出した[T; N]::map()は配列を配列に変換するのみでイテレータと一切関係ない
我々はmap()に関しては一貫してIterator::map()の話しかしていない
37:デフォルトの名無しさん
21/11/08 08:32:39.74 fuBvo+92.net
ほんとここに来るやつは皆を騙す詐欺師の気持ちや悪い奴ばかり、一切関係ないぞ!
38:デフォルトの名無しさん
21/11/08 10:16:54.73 csyRALBm.net
どんなに適当に書いてもコンパイラさんがうまいこと最適化してくれるのでヨシ!
39:デフォルトの名無しさん
21/11/08 10:32:52.09 xhkp7neH.net
最初から大して違いも出ないが、分かりやすさを重視するなら高階関数かな?
これはこう書け!と口煩くいってくる奴が理解できないけど…
でも裾野を広げるためには未熟で冗長なコードでも許容しなきゃね、1つもfor無しで
デカいプロジェクトを書くことなんて無いし、趣味でやってるだけなら縛りもありだが
40:デフォルトの名無しさん
21/11/08 10:39:48.75 XWIn4U6b.net
高階関数でかくと並列化しやすいからそう書いてる
41:デフォルトの名無しさん
21/11/08 11:16:30.19 NiBBbin2.net
>>39
rayonなどに移行しやすいという意味?
42:デフォルトの名無しさん
21/11/08 11:23:59.38 OCI2yhfb.net
rustの得意とする高パフォーマンス領域だとactix(-web)なんかはスレッド数=物理コア数で
下手に並列化しないほうがrequest/secは高いから、標準の高階関数も並列にはなっていない
訳で、バッチ処理みたいなその時に他の処理が走ってなくて、如何に処理時間を短くするか
というプログラムなら最初からRayonとか最初に導入してそう…
状態共有しない並列起動だけなら、thread::spawn(|| { for i in 0..x {...} }でも比較的に
簡単に出来るが、そういう意味では無いんだろう
43:デフォルトの名無しさん
21/11/08 11:28:09.63 hB1EvZ1K.net
さすが長文w
中身がナッシング
44:デフォルトの名無しさん
21/11/08 12:29:17.17 QzkUvy+x.net
>皆を騙す詐欺師の気持ち悪い奴ばかり
都合悪くなると話題変えて逃げるしな
45:デフォルトの名無しさん
21/11/08 12:33:26.36 4QnDXIIG.net
async/awaitが高階関数と(今の所)相性良くない
46:デフォルトの名無しさん
21/11/08 12:45:54.28 ODzJsaPg.net
話題変えて逃げてんじゃねーよ!カス!
47:デフォルトの名無しさん
21/11/08 13:17:11.03 NiBBbin2.net
>>44
今のところとしてるのは改善策が取り込まれる見込みだから?
それとも単に将来のことはわからない程度の意味?
48:デフォルトの名無しさん
21/11/08 13:48:44.02 vASCcAjA.net
静的チェックでオールOKみたいな言語って意識高い系のバカを呼び寄せやすいんだよ。
奴らは動的テストなんてしたくないって意識をすぐ誤魔化す癖がついてる。
49:デフォルトの名無しさん
21/11/08 14:48:38.73 xhY6YpeY.net
ストローマンがあらわれた
50:デフォルトの名無しさん
21/11/08 14:51:34.92 vASCcAjA.net
自分のことを言われて怒っちゃったのか。
わかりやすいね。
51:デフォルトの名無しさん
21/11/08 14:53:05.69 M7ed0MM2.net
>>32
ストリングとストラ
もしくは
ストリングとストリングスライス
52:デフォルトの名無しさん
21/11/08 15:29:22.26 GGqoyJW1.net
うん、完全理解した。こんスレは同じrustガンダム乗りなのに連邦の白い悪魔以外は認めないんだ
どっちでもいいじゃんか・・・
URLリンク(i.imgur.com)
53:デフォルトの名無しさん
21/11/08 17:38:09.25 7WV3o+af.net
Vecに入ったi32(空っぽの可能性もあり)の平均を計算するのってどうするのが一番スマート?
54:デフォルトの名無しさん
21/11/08 17:43:46.40 Chf4/Vgn.net
高階関数使う
55:デフォルトの名無しさん
21/11/08 20:19:03.35 QIO9hdEn.net
副作用持つときはfor文でそれ以外はメソッドチェーン繋げてやります
56:デフォルトの名無しさん
21/11/08 21:14:06.85 6BzlB5w0.net
>>34
イテレータの高階関数の連続使用でメモリーを圧迫することはないと思います
各イテレータ自体はコンパクトな構造体ですし高階関数自体の処理も基本的にはメモリーを圧迫するものではありません
ただし全ての要素を受け取らないと算出できない物もあるため一時的に長いVecを使うことになるイテレータもあります
この場合はイテレータを用いずに自分でforなどを回す方法をとっても同様にして一時的に長いVecを使うことになるでしょう
57:デフォルトの名無しさん
21/11/08 21:14:39.00 IC/lLBzS.net
そういや平均求めるメソッドってstd::iterにもitertoolsにもないんか?
58:デフォルトの名無しさん
21/11/08 21:38:29.05 NiBBbin2.net
>>47
Rustには動的テストを容易にする仕組みが入ってるしRustの話ではなさそうだ
59:デフォルトの名無しさん
21/11/08 21:38:34.43 3AO0oG+L.net
そんな簡単な実装を一々stdに入れてたら、糞関数だらけのLLライクになるじゃん?
60:デフォルトの名無しさん
21/11/09 04:01:57.70 d6arxLIn.net
>>58
std::iterに入れろとまでは思わないけど、
sumもcountもイテレータ消費しちゃうから微妙にめんどくさくない?
61:デフォルトの名無しさん
21/11/09 20:24:06.75 E/uEHC7F.net
>>59
こんな感じでsumとcountを同時に計算かな
trait Average<T> {
fn average(self) -> T;
}
impl<I: Iterator<Item=T>, T: Zero + One + Add<Output=T> + Div<Output=T>> Average<T> for I {
fn average(self: I) -> T {
let (cnt, sum): (T, T) = self.fold((Zero::zero(), Zero::zero()), |(cnt, sum), n| (cnt + One::one(), sum + n));
sum / cnt
}
}
fn main() {
assert_eq!(500, (1..1000).into_iter().average());
assert_eq!(6.8, vec![2.3, 8.7, 9.4].into_iter().average());
assert_eq!(33, [1, 3, 5, 7, 9].into_iter().map(|n| n*n).average());
}
62:デフォルトの名無しさん
21/11/09 22:53:03.40 yHbe3Xjs.net
オーバーフローしないように型を選択することとか、
汎用的に使えて問題を避けることを考えたら平均を計算するのってそんなに簡単な話ではないと思う。
63:デフォルトの名無しさん
21/11/10 08:15:17.85 BLadVH6y.net
stats::median
今でもfilter使えだとかうっせーのばっかり、標準ライブラリは小さくて良いYO
URLリンク(lib.rs)
statrs-dev/statrsとか好きなの引っ張ってきて入れろよ?平均じゃなくてもさ
64:デフォルトの名無しさん
21/11/12 17:23:46.15 pYLNCtl5.net
Vecとかに入った複数のtokio::task::JoinHandleをjoinするのはどうやるんでしょうか?
65:デフォルトの名無しさん
21/11/12 20:00:37.76 PXROpwUc.net
thread::JoinHandleは並列で(マルチスレッドとなり)
join()で待つ
task::JoinHandleは非同期で(シングルスレッド時は並行、マルチスレッド時は並列となり)
awaitで待つ
66:デフォルトの名無しさん
21/11/12 21:00:47.58 CuMBVxkM.net
futures::future::join_allでいいんじゃね
67:デフォルトの名無しさん
21/11/12 23:59:03.69 BY1dkyYH.net
task::JoinHandleの方はFuture trait実装なので
列挙ならfutures::join!(f1,f2,f3,...)マクロ
VecならIntoIteratorに対応のjoin_all(v) >>65
68:デフォルトの名無しさん
21/11/17 00:04:41.47 6Xk/Jjaa.net
おいウンコ引き取りに来いよ
69:デフォルトの名無しさん
21/11/20 19:33:51.68 i03LC++E.net
let mut a = 'a';
let mut d = 'd';
match 'b' {
a..=d => { println!("true") }
_ => { println!("false") }
}
matchでcharの範囲指定ができるからって試したんだけど、
変数に入れたcharだと範囲指定ってできないの?
これだとあんまり意味ないような
70:デフォルトの名無しさん
21/11/20 20:32:13.75 iQUqB4pW.net
パターンには代入される側の変数しか書けないけどガードは自由自在
fn test(b: char) -> bool {
let a = 'a';
let d = 'd';
match b {
_ if a <= b && b <= d => true,
_ => false,
}
}
fn main() {
assert_eq!(test('b'), true);
assert_eq!(test('x'), false);
}
71:デフォルトの名無しさん
21/11/20 20:43:54.76 iQUqB4pW.net
そして範囲指定を使いたいんだったらこう
_ if (a..=d).contains(&b) => true,
72:デフォルトの名無しさん
21/11/20 20:49:00.01 iQUqB4pW.net
bのところが式かも知れないのでxにマッチさせてこの方がベターか
x if (a..=d).contains(&x) => true,
73:デフォルトの名無しさん
21/11/20 21:28:17.44 i03LC++E.net
>>69
ありがとう
if (a..=b).contains(&c) { println!("true") } else { println!("false"); };
って感じで試して見たけど、ガードで
(a..=b).contains(&c)にすればよさげだとわかった
74:デフォルトの名無しさん
21/11/21 01:21:54.59 VYuGYhJz.net
Rangeを含むジェネリックな関数を作りたいのですが例えば単純化した例
fn ntimes_print<T: num::Integer>(n: T, s: &str) {
(0..n).for_each(|_| print!("{}", s));
}
ここでRangeのnがexpected integerと言われエラーになってしまい
上記のように型Tにnum::Integerトレイトを制約してみましたが上手くいきません
どうすれば型TとジェネリックのままRangeを使えるでしょうか?
75:デフォルトの名無しさん
21/11/21 01:26:47.07 mkw0m2hl.net
>>73
複数のトレイトは + で繋げることができるんやで
76:デフォルトの名無しさん
21/11/21 01:30:30.33 VYuGYhJz.net
>>74
はい
それで何のトレイトを繋げればいいですか?という質問です
77:デフォルトの名無しさん
21/11/21 02:02:32.44 VYuGYhJz.net
格闘しているうちにコンパイラがstableじゃダメだとかfeature指定しろとか無茶を言って来るので従ってnightlyにしたりfeature指定したりしたところようやく動いたのですが何か変ですよね
78:デフォルトの名無しさん
21/11/21 02:16:44.28 qtUeCPjG.net
うーん
引数に繰り返す回数渡せばいいような気がするんだけどそう簡単じゃないのね
79:デフォルトの名無しさん
21/11/21 02:30:42.73 VYuGYhJz.net
nightlyにせずstableのままで範囲指定nをジェネリック化できた方いましたらやり方教えて下さい
80:デフォルトの名無しさん
21/11/21 02:59:34.97 mkw0m2hl.net
>>75
ドキュメントをみるかぎり PartialOrd が要求されているでよ
81:デフォルトの名無しさん
21/11/21 03:09:03.92 qtUeCPjG.net
やりたいことってこんな感じ?
URLリンク(play.rust-lang.org)
82:デフォルトの名無しさん
21/11/21 04:34:14.82 VYuGYhJz.net
>>79
入れたけどダメでした
>>80
違う
この形です
fn ntimes_print<T>(n: T, s: &str) {
(0..n).for_each(|_| print!("{}", s));
}
83:デフォルトの名無しさん
21/11/21 10:25:42.94 szj4saah.net
リテラル`0`の問題とstd::iter::Stepがunstableなのと2つ問題を解決する必要がある
fn ntimes_print<T>(n: T, s: &str)
where T: num::Integer + num::ToPrimitive + Clone
{
num::range(T::zero(), n).for_each(|_| print!("{}", s));
}
84:デフォルトの名無しさん
21/11/21 11:02:09.61 VYuGYhJz.net
やはり..を使う限りstableでは無理でnightlyでないと以下のような素朴な実装も無理ということでしょうか
少し例を実用的に変えてみましたがトレイト境界(制約)を最小限で以下のようなコードでnightlyだと動いています
#![feature(step_trait)]
fn main() {
let n = 5; // 任意の整数型
n.times(|n| println!("OK {}", n));
}
trait Times<T: Sized> {
fn times<F>(self, f: F) where F: FnMut(T) -> ();
}
impl<T> Times<T> for T where T: num::Zero + std::iter::Step {
fn times<F>(self: T, f: F) where F: FnMut(T) -> () {
(num::Zero::zero()..self).for_each(f);
}
}
85:デフォルトの名無しさん
21/11/21 12:10:20.71 qtUeCPjG.net
URLリンク(play.rust-lang.org)
86:デフォルトの名無しさん
21/11/21 15:03:50.15 szj4saah.net
>>83
where
T: num::Integer,
Range<T>: Iterator<Item=T>,
とかにすればできるよ
87:デフォルトの名無しさん
21/11/21 16:08:27.58 VYuGYhJz.net
>>85
凄い!stableで..で動きました!ありがとう!
impl<T> Times<T> for T where T: num::Zero, std::ops::Range<T>: Iterator<Item=T> {
fn times<F>(self: T, f: F) where F: FnMut(T) -> () {
(num::Zero::zero()..self).for_each(f);
}
}
88:デフォルトの名無しさん
21/11/21 18:59:28.56 a8amZ/lG.net
更にtimes()自体をイテレータにしてしまえば汎用的になるだけでなく
それらトレイト境界などのコードの記述も魔法のように消えて短くなる
fn main() {
let n = 5; // 任意の整数型
n.times().for_each(|n| println!("OK {}", n));
}
trait Times<T: Sized> {
fn times(self) -> std::ops::Range<T>;
}
impl<T: num::Zero> Times<T> for T {
fn times(self: T) -> std::ops::Range<T> {
num::Zero::zero()..self
}
}
これだけで動作する
89:デフォルトの名無しさん
21/11/21 20:22:13.92 ekMm5ue5.net
timesなら精々u64::MAX回も繰り返すことなさそうだしTからu64に変換するのではだめなの?
90:デフォルトの名無しさん
21/11/21 20:32:22.18 VYuGYhJz.net
トレイト境界が短くなって素晴らしい原因ですがもしかして
>>83では要のops::Range<T>型がコードに明示されてないので条件のiter::Stepを要求されてしまい
>>85ではそのops::Range<T>型をトレイト境界に登場させたためiter::Stepが不要となり
>>87ではそのops::Range<T>型を返り値として明記したためトレイト境界にも不要となった??
91:デフォルトの名無しさん
21/11/22 10:01:45.21 YMaXH3oe.net
指定するトレイト境界が減った代わりにAPIが劣化してる
92:デフォルトの名無しさん
21/11/22 11:46:24.84 EEj8G+es.net
>>90
イテレータ版の方がfor_each以外とも組み合わせられるからAPIとして良いと思う
>>87
しかしトレイト境界でnum::Zeroしか求められないのはstd::ops::Range周りの設計がおかしいと思われる
普通に実装すれば初期値(num::Zero)に増分(num::One)を加えて(ops::Add)いって比較(ops::PartialOrd)が必要となる
実際にnum::rangeによるイテレータ版times()の実装は Clone + PartialOrd + num::Zero + num::One となる
fn main() {
let n = 5; // 任意の整数型
n.times().for_each(|n| println!("OK {}", n));
}
trait Times<T: Sized> {
fn times(self) -> num::iter::Range<T>;
}
impl<T: Clone + PartialOrd + num::Zero + num::One> Times<T> for T {
fn times(self: T) -> num::iter::Range<T> {
num::range(T::zero(), self)
}
}
93:デフォルトの名無しさん
21/11/22 12:13:59.22 qBbb57Hy.net
わざわざ外部crateと独自trait使って
n.times().for_each(f)にするくらいなら
(0..n).for_each(f)で十分
94:デフォルトの名無しさん
21/11/22 12:33:40.60 EEj8G+es.net
>>92
それでは最初の条件のジェネリックを満たせていない
>>88
状況によってはそのように強引にu64へ変換できても対応できなくなるケースもある
例えば単純な例として文字'x'からのみなる文字列による型Xを考えてみよう
#[derive(Debug,Clone,PartialEq,PartialOrd)]
struct X(String);
impl X {
fn new(s: &str) -> Self {
if !s.chars().all(|c| c == 'x') {
panic!("not x");
}
X(s.to_string())
}
}
これで文字'x'以外は使えない文字列の型が出来上がり
あとは>>91で必要なZeroとOneとAddを定義すれば動くはず
impl num::Zero for X {
fn zero() -> X { X::new("") }
fn is_zero(&self) -> bool { self.0 == "" }
}
impl num::One for X {
fn one() -> X { X::new("x") }
}
impl std::ops::Add for X {
type Output = X;
fn add(self, rhs: X) -> X { X(self.0.clone() + &(rhs.0)) }
}
95:デフォルトの名無しさん
21/11/22 12:45:37.77 EEj8G+es.net
>>93の続き
ところがnumクレートのOneは不必要に掛け算のMulも要求してきた
仕方ないので呼び出したらパニックするimplを加える
impl std::ops::Mul for X {
type Output = X;
fn mul(self, _rhs: X) -> X {
panic!("mul() for X")
}
}
さらになぜかnum::ToPrimitiveも要求してきたのでこれもパニック実装する
impl num::ToPrimitive for X {
fn to_i64(&self) -> Option<i64> {
panic!("to_i64() for X")
}
fn to_u64(&self) -> Option<u64> {
panic!("to_u64() for X")
}
}
これで>>91のnum::range利用イテレータ版times()が動くはず
そういえばDisplay実装を忘れたのでDebug表示
fn main() {
let n = X::new("xxxxx");
n.times().for_each(|n| println!("OK {:?}", n));
}
実行結果:
OK X("")
OK X("x")
OK X("xx")
OK X("xxx")
OK X("xxxx")
ちゃんと数値型以外でも動きました
96:デフォルトの名無しさん
21/11/22 15:04:22.59 UzgCqcLK.net
>>93
>それでは最初の条件のジェネリックを満たせていない
ジェネリックにしたければ(T::zero()..n).for_each(f)と書けばいいだけでしょ
単にRangeを返すだけのメソッドを手間かけて微妙に抽象化しても周りが迷惑するだけだぞ
97:デフォルトの名無しさん
21/11/22 15:23:28.68 EEj8G+es.net
>>95
ところがT::zero()..nだと動かない
さきほどの>>93の型Xはnum::range利用だと動いたが
let n = X::new("xxxxx");
(X::zero()..n).for_each(|n| println!("OK {:?}", n));
としようとすると以下のコンパイルエラー
the following trait bounds were not satisfied:
`X: Step`
つまりnightlyでないと使えないstd::iter::Stepを満たしていないと言われる
98:デフォルトの名無しさん
21/11/22 19:21:50.63 fRCpO7Rh.net
どうしてもstableでやりたいという話ならRangeとStepを独自に用意するしかなさそう
n..mという表記は使えないが、n.times()なら支障なく実装できるかと
自分なら以下みたいに書くけどね
(T: From<i32> + PartialOrd が前提)
(0..).map(T::from).take_while(move ¦x¦ x<n)
99:デフォルトの名無しさん
21/11/22 19:36:28.33 3Rtka3dv.net
なるほどね
100:デフォルトの名無しさん
21/11/22 19:55:09.41 5egSOJea.net
>>97
それだとT: From<i32>という無関係で不要な強い条件を満たさないといけないため
例えば>>93の型Xでは動かないね
シンプルにZero、One、Add、PartialOrdだけで実装したほうが良さそう
101:デフォルトの名無しさん
21/11/22 20:04:43.45 VyYbXHRo.net
C++じゃないんだから不毛な型パズルはやめろ
102:デフォルトの名無しさん
21/11/22 20:25:52.53 gBMgBg1g.net
C++よりマシ
103:デフォルトの名無しさん
21/11/22 20:56:45.47 MJpN6tlo.net
意味のない例で延々とよくやるわ
104:デフォルトの名無しさん
21/11/22 21:02:15.85 FLi/1Joa.net
カスタムな型ならRangeよりイテレータ対応が先
105:デフォルトの名無しさん
21/11/22 21:09:17.05 fRCpO7Rh.net
StepがunstableなのはさておきFromLiteralみたいなトレイトがあるとZeroやOneの出番が減ってうれしいのかね
T: FromLiteralの時に整数リテラルがT型の値として解釈されるようになるようなイメージ
106:デフォルトの名無しさん
21/11/22 21:58:50.69 FLi/1Joa.net
try_into()でできるよ
107:デフォルトの名無しさん
21/11/22 22:09:34.28 fRCpO7Rh.net
言葉足らずでしたね
struct Foo;
が FromLiteral を実装しているときに
let n: T = 123;
というコードを書くとコンパイラが
let n = T::from_literal("123");
といったコードに変換してくれるイメージ
from_literalはconst fnにできてコンパイル時にエラー検出できるとベター
108:デフォルトの名無しさん
21/11/22 22:10:11.00 fRCpO7Rh.net
>>106
TはFooの間違い
109:デフォルトの名無しさん
21/11/23 07:57:08.78 9DtS3af5.net
ねこ
110:デフォルトの名無しさん
21/11/23 10:28:33.41 8Ju98kPx.net
URLリンク(github.com)
Coreチームがクソだからやめたった、ってこと?
111:デフォルトの名無しさん
21/11/23 13:56:03.32 b1gEfTjX.net
const fnが言いたいだけやろ、だれが=演算子でcopyでもなく、言語上ないことになってるコンストラクタでもなく
そんな特異なトレイトを勝手に呼ぶのが嬉しいねん、なにがバター犬や
112:デフォルトの名無しさん
21/11/23 14:58:36.02 s6k3uLQ1.net
>>110
= が特殊な振る舞いをするのではなくて
整数リテラルが組み込み整数型についてgenericな数値になるという振る舞いを
FromLiteralを実装した型に広げようという案のつもりです
113:デフォルトの名無しさん
21/11/23 20:05:14.41 1c3aeddQ.net
m..nをiter::Step使わず素直にPartialOrd + One + Addだけで実装してくれれば汎用的で分かりやすいと思う
struct Range<T> {
start: T,
end: T,
}
fn range<T>(start: T, end: T) -> Range<T> {
Range { start, end }
}
impl<T: Clone + PartialOrd + One + Add<Output=T>> Iterator for Range<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.start < self.end {
let result = self.start.clone();
self.start = self.start.clone() + T::one();
Some(result)
} else {
None
}
}
}
fn main() {
let n :Vec<u8> = range(1, 5).collect();
let x :Vec<X> = range(X::new("x"), X::new("xxxxx")).collect();
println!("{:?}", n); // [1, 2, 3, 4]
println!("{:?}", x); // [X("x"), X("xx"), X("xxx"), X("xxxx")]
}
>>93のX型でも動いたよ
114:デフォルトの名無しさん
21/11/23 20:38:59.43 rocYZd+S.net
特徴もないリテラルを勝手に解釈するとかあり得んわ
現状だって0b0011u32とか0x80u32とか書いてるのに、型定義が横にあるからそれでコンパイル時に
パース処理したいなんてそんな都合の良い言語ちゃうだろ、Raw stringのr"123"とか、Raw bytesとか
とも違うし、確かにfrom_strが"123"を解釈するけどやるにしても、let x: i32 = "123";が通ってから。
でもコンパイル時とはいえ自動型変換に見えるコードは承認しないと思うし、直行性も下がる
こんな場末の酒場みたいな所で言ってもコミッターどころかforと高階関数で揉める駄スレにどうこう出来る内容ちゃう
115:デフォルトの名無しさん
21/11/23 20:58:16.83 Oek7vlRG.net
>>112
数値だからAdd+Oneが成り立つわけで
型Xとか何の意味もないぞ
116:デフォルトの名無しさん
21/11/23 22:14:31.32 qrGqDm2c.net
>>114
でも現実のプログラミングでは数値を生で使うよりも
struct Counter { usize } とか
struct Age { usize } とか
struct XxxLength { usize } とかにして
型が異なることをはっきりさせて安全に使いますよね
そして付加情報があればstructのフィールドが複数になることもあったり
あるいは
117:struct std::time::Durationのようにnanoからsecまで扱えるようにしてもAddをimplして使いますよね つまり生の数値だけを対象にしていては視点が狭いと思うのです
118:デフォルトの名無しさん
21/11/23 22:15:57.50 qrGqDm2c.net
>>115の{ }は( )の誤記ですw
119:デフォルトの名無しさん
21/11/24 03:12:15.58 P1gN11rG.net
Stepがstable入りしたら要らなくなる話になにとんでもない破壊的変更を持ち出しているんだ
120:デフォルトの名無しさん
21/11/24 03:25:07.43 gceGN8+W.net
Age型をn.times().for_each()
Length型をn.times().for_each()
Duration型をn.times().for_each()
ジェネリックw
121:デフォルトの名無しさん
21/11/24 05:02:23.28 e1u6MioL.net
>>118
timesより>>112のm..n rangeイテレータが汎用的でいいんじゃね?
122:デフォルトの名無しさん
21/11/24 15:22:53.32 zkfKZqQ7.net
>>119
times()ですら無駄なのに>>112とかありえないよ
無価値どころか害悪レベルなんだけど
123:デフォルトの名無しさん
21/11/24 17:05:44.23 5wn/1hS5.net
>>117
コンパイラや標準ライブラリの変更で型推論の結果が変わることは破壊的変更扱いされなかったっけ
124:デフォルトの名無しさん
21/11/24 17:26:56.67 Zq3lnaBh.net
>>106
なんとなく分かりましたがいきなりコンパイラが自動変換の前に現状で
例えばまずは整数型を例に絞ってやるとして
trait IntegerCompatible {
type Integer;
fn into_integer(&self) -> Self::Integer;
fn from_integer(n: Self::Integer) -> Self;
}
こんな感じのトレイトにして
まずは利便性のために整数型自体に自分を返すよう実装しておいて
macro_rules! integer_compatible {
($($t:ty)*) => ($(
impl IntegerCompatible for $t {
type Integer = $t;
#[inline]
fn into_integer(&self) -> Self::Integer {
*self
}
#[inline]
fn from_integer(n: Self::Integer) -> Self {
n
}
}
)*)
}
integer_compatible! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
あとは今はコンパイラ支援がないので
使う時に自分でinto_integerして計算などして結果をfrom_integerする感じですかね
125:デフォルトの名無しさん
21/11/24 17:39:27.77 Zq3lnaBh.net
>>122の続き
実際に使ってみる具体例として面倒なので>>112をそのまま使うと
struct Range<T> {
start: T,
end: T,
}
このあたりはそのまま使うとして
fn range<T>(start: T, end: T) -> Range<T> {
Range { start, end }
}
実装部分ではジェネリックT型にOneやAddなどを求められていたのを
CloneとIntegerCompatible要求だけに変えて整数型IにOneやAddなどを移動
impl<T, I> Iterator for Range<T> where
T: Clone + IntegerCompatible<Integer=I>,
I: Copy + PartialOrd + num::One + std::ops::Add<Output=I>, {
type Item = T;
fn next(&mut self) -> Option<T> {
if T::into_integer(&self.start) < T::into_integer(&self.end) {
let result = self.start.clone();
self.start = T::from_integer(T::into_integer(&self.start) + I::one());
Some(result)
} else {
None
}
}
}
今はコンパイラ支援がないので手動変換ですが自動もしくは簡便な記法に出来そう
あと整数型にはいちいちOneやAddやPartialOrdを書かなくても済むように出来そう
126:デフォルトの名無しさん
21/11/24 17:47:29.36 Zq3lnaBh.net
>>123の続き
念のため実際に使う時にどうなるかと使えるかを確認すると
let n :Vec<u128> = range(3, 7).collect();
println!("{:?}", n); // [3, 4, 5, 6]
もちろん整数型自体は使えるのは当たり前なのでLength(usize)で使う場合
#[derive(Debug,Clone)]
struct Length(usize);
impl IntegerCompatible for Length {
type Integer = usize;
fn into_integer(&self) -> Self::Integer {
self.0
}
fn from_integer(n: Self::Integer) -> Self {
Length(n)
}
}
このIntegerCompatible定義はこのような単純形ならマクロ化で出来そうですね
let v :Vec<Length> = range(Length(3), Length(7)).collect();
println!("{:?}", v); // [Length(3), Length(4), Length(5), Length(6)]
そして当然ながら動きました
127:デフォルトの名無しさん
21/11/24 17:56:43.20 Zq3lnaBh.net
>>124の続き
あとは>>93に出てきた変なX型ですね
#[derive(Debug,Clone)]
struct X(String);
impl X {
fn new(s: &str) -> Self {
if !s.chars().all(|c| c == 'x') {
panic!("not x");
}
X(s.to_string())
}
}
と定義はそのまま使っておきます
あとはOneやAddの実装はをせずにIntegerCompatibleだけ実装
impl IntegerCompatible for X {
type Integer = usize;
fn into_integer(&self) -> Self::Integer {
self.0.len()
}
fn from_integer(n: Self::Integer) -> Self {
X::new(&std::iter::repeat("x").take(n).collect::<String>())
}
}
このような特殊例のみIntegerCompatible実装のマクロ化は無理ですね
let v :Vec<X> = range(X::new("xxx"), X::new("xxxxxxx")).collect();
println!("{:?}", v); // [X("xxx"), X("xxxx"), X("xxxxx"), X("xxxxxx")]
当然ですがX型についても動きました
128:デフォルトの名無しさん
21/11/24 18:18:05.49 AgL0hXz4.net
誰も見てねーから別のところでやれ
129:デフォルトの名無しさん
21/11/24 19:13:18.31 5wn/1hS5.net
話題がないんだもん
なんか話題を提供してくれ
130:デフォルトの名無しさん
21/11/24 23:19:00.01 e1u6MioL.net
結局Derefみたいにコンパイラが自動的に適用して変換してくれればそのintoやfromをプログラムには書かなくて済むんやろ
131:デフォルトの名無しさん
21/11/25 23:08:27.91 QVGqalzl.net
ファイルを開く操作って普通に考えたらFile::open_ほにゃらら()みたいなメソッドにOpenOptionsを渡すほうが自然だと思うんですが
OpenOptions::open()みたいな方法を取ってるのってどういう理由からなんでしょうか?
132:デフォルトの名無しさん
21/11/25 23:59:01.72 88pS2ZzI.net
>>129
・その方がメソッドチェーンで見やすい
・複雑な構造体を用意してそこに様々な値をセットして指定するインターフェースは非常に大変
・そのうえOS毎に違う部分もある
133:デフォルトの名無しさん
21/11/26 00:22:31.18 aH1+xhzE.net
>>129
Rustは関数定義でデフォルト値のあるオプション引数をサポートしてないから
オプション引数的な使い方をしたい場合はビルダーパターンを使うのが一般的
File::options().read(true).create(true).open("foo.txt”);みたいな使い方になる
134:デフォルトの名無しさん
21/11/26 09:37:38.31 kuMbCEJE.net
うーん
ダサいな
もっときれいにならんの?
135:デフォルトの名無しさん
21/11/26 09:50:01.95 kuMbCEJE.net
メソッドチェーンって過去の遺物だよね?
バグの温床だし
長いとどこかで消し忘れや二重指定が出て本人が気が付かなくなる
長くなっただけで破綻するんだからおかしい
136:デフォルトの名無しさん
21/11/26 10:00:30.74 5+U4u14D.net
>>133
一般的に今どきのプログラミング言語は全てメソッドチェーンが主流
もしかしてメソッドチェーンを使わない古い言語使いの方ですか?
137:デフォルトの名無しさん
21/11/26 10:06:22.27 yaO+xNFa.net
パイプライン演算子大好きとかLisp方面から来た人とかなのかも……
138:デフォルトの名無しさん
21/11/26 10:12:22.08 kuMbCEJE.net
え?メソッドチェーンってjqueryが流行ってたころの名残でしょ?
10年ぐらい前
メソッドチェーンなんて書いててだるいだけ
139:デフォルトの名無しさん
21/11/26 10:21:37.95 5+U4u14D.net
>>136
JavaScriptも今は関数型プログラミングが主流へと変わりメソッドチェーンだし外部ライブラリを使うインタフェースもメソッドチェーンがよく使われる
そしてRustも同様
どこの古い世界から来たお客さんですか?
140:デフォルトの名無しさん
21/11/26 10:34:43.95 E7I1X7f8.net
メソッドチェーンって関数型由来ではないし
141:デフォルトの名無しさん
21/11/26 11:18:59.37 /IsoxS9R.net
>>133
ビルダーは二重指定しても問題ない
消し忘れはどういう指定方法でも発生するからテストで防ぐ以外ないよ
テスト書かない文化の人?
142:デフォルトの名無しさん
21/11/26 11:23:55.78 /IsoxS9R.net
>>135
パイプ演算子も同じ問題抱えてるし
Lispでも今はthreadマクロで処理順に書く
143:デフォルトの名無しさん
21/11/26 11:31:53.80 oSCWFWAt.net
>>140
はえ~、そうか勉強になったわ
144:デフォルトの名無しさん
21/11/26 11:44:41.28 kuMbCEJE.net
>>139
二重指定は一方でtrue一方でfalse指定しているパターン
145:デフォルトの名無しさん
21/11/26 12:05:35.16 SqSfLhr2.net
>>132
どうやったら綺麗なのか参考のため教えて
Rustじゃなくて他の言語でも
仮想の言語でもいいよ
146:デフォルトの名無しさん
21/11/26 12:25:05
147:.02 ID:kuMbCEJE.net
148:デフォルトの名無しさん
21/11/26 12:29:19.27 kuMbCEJE.net
これが美しいと言う人はそれこそ逆ポーランド次元から来た異次元人だと思うよ
149:デフォルトの名無しさん
21/11/26 12:40:54.22 R0yJ4Kup.net
なかなか極端な例を出してきたな……
150:デフォルトの名無しさん
21/11/26 12:43:09.59 GoGODfBQ.net
おまいら日本語否定ですか。
>>144は設計が悪いんじゃない?
File::options().readwrite().newfile().open("foo.txt”);
なら自然だろ。
>>144
151:デフォルトの名無しさん
21/11/26 12:51:53.22 kuMbCEJE.net
逆ポーランド人現る
152:デフォルトの名無しさん
21/11/26 12:53:26.61 kuMbCEJE.net
ファイルを開こうと思うときにoptions型から思考がスタートする人は天才なんだろうな
それか飼いならされた人
153:デフォルトの名無しさん
21/11/26 12:57:20.12 kuMbCEJE.net
言語否定じゃなくてライブラリ設計がおかしい
実用無視の人が設計するとこうなる
154:デフォルトの名無しさん
21/11/26 12:58:29.05 Q6WyUjPa.net
File::open_with_options("foo.txt", &OpenOptions::new().read(true).create(true));
どっちが良いとか悪いとかじゃないと思うがなあ
155:デフォルトの名無しさん
21/11/26 13:13:18.47 kuMbCEJE.net
美しくないだろ普通に
156:デフォルトの名無しさん
21/11/26 13:13:45.37 HMe+psgI.net
OpenOptionsって名前で明らかに「ファイル開く時のオプションですよ」って名前なのにファイル開く操作まで持ってるから気持ち悪いんだよな
同じモジュールでディレクトリはDirBuilderとかあるんだから普通にFileBuilderとかにすりゃええやんとか思っちゃうけどなんか理由があったんかね
157:デフォルトの名無しさん
21/11/26 13:23:36.09 kuMbCEJE.net
確かに他だったらoptionのインスタンスを作ってopenに食わせる感じだな
それもどうかと思うけど
ほぼ定数みたいなものをわざわざ作って食わせるなんて
158:デフォルトの名無しさん
21/11/26 14:04:50.29 TIzT5fn9.net
慣れない人にとっては分かりにくいAPIにならざるを得ないのは確か
ただ他の言語でオプション引数やオーバーロードが2桁あるのが当たり前になってるようなライブラリを見るとそれぞれ一長一短あるなとは思う
159:デフォルトの名無しさん
21/11/26 14:15:29.08 Q6WyUjPa.net
>>154
associated constくらい用意してもらってもいいかもね
160:デフォルトの名無しさん
21/11/26 14:20:28.16 Ye0bskEh.net
>>153
OpenOptionsはrust1.0以前からあるものなのでその頃はビルダーのイディオムがなかったのだと思う
DirBuilderはrust1.6で追加されたものなので比較的新しい
161:デフォルトの名無しさん
21/11/26 14:57:24.65 AxmLr4ZJ.net
> 通常のCのオープン関数のほうが100倍キレイで簡潔で合理的
さっぱりわからんけどw
別に
File::options().readwrite().newfile().open("foo.txt”);
がいいとも悪いとも思わんし
cのopenがいいとも悪いとも思わん
どんな素晴らしいopen見せてくれるのかと思ったらガッカリした
162:デフォルトの名無しさん
21/11/26 15:03:20.08 BGloBCeB.net
OpenOptionsが分かりにくいってのはそのとおりだと思うし、実際みんなそう思ってるみたいで
ちょうどFile::optionsにするRFCが通るところだよ
163:デフォルトの名無しさん
21/11/26 16:18:02.52 Ye0bskEh.net
>>159
stabilizeのPRが10日前に取り込まれたから1月にはリリースされそう
164:デフォルトの名無しさん
21/11/26 18:16:06.66 kuMbCEJE.net
>>158
FILE * fopen(const char * filename, const char * mode);
こっちの方が100倍良い
165:デフォルトの名無しさん
21/11/26 19:18:40.42 wVYXipKz.net
>>161
それはfopenの仕様(引数の意味とか)を知っているのが前提だからなぁ。
filenameとmodeの順番を間違えたら読み間違える。
メソッドチェーンと比較するなら名前付き引数のメソッド呼び出しじゃない?
あるいはインテリセンス環境下という条件付きか。
166:デフォルトの名無しさん
21/11/26 19:21:46.75 Hq7eoo6P.net
>>161
ほんそれ
167:デフォルトの名無しさん
21/11/26 19:26:21.35 kuMbCEJE.net
仮想のコードで例が適切かどうかわからないけど
File::options().readwrite().newfile().read().open("foo.txt”)
と書いて直後で書き込めねえええええよと叫ぶよりfopenの方がいいだろ;
168:デフォルトの名無しさん
21/11/26 19:37:54.04 kuMbCEJE.net
あ、ミスった
それはさておき例のメソッドチェーン見ても瞬時に不安しかよぎらない
readwrite() が中で read(true).write(true) してるとして
readonly() が中でread(true)しかしてないんじゃないかとか不安
169:デフォルトの名無しさん
21/11/26 19:47:03.96 AxmLr4ZJ.net
>>162
> 名前付き引数のメソッド呼び出しじゃない?
俺も最初はそう言う話が出てくるのかとおもったら
cのopenを有りがたがってる奴が出てきて呆れた
170:デフォルトの名無しさん
21/11/26 20:09:04.42 XtGzaRsE.net
>>134
メソッドチェーンと関数チェーン|パイプラインが区別できてないんでは?
メソッドチェーンってレシーバの記述を省略できるとか文の数が減るとかでしかないでしょ。
171:デフォルトの名無しさん
21/11/26 20:37:35.34 kuMbCEJE.net
>>166
fopenとゴミみたいなメソッドチェーン
どっちがバグの温床だ?
172:デフォルトの名無しさん
21/11/26 20:42:02.78 kuMbCEJE.net
cのfopenはメソッドチェーンより簡潔だ
本当は他の言語のenumでいいんだけど
Open("rust.txt",FileMode.Open, FileAccess.Read)
これでバグは出ない
173:デフォルトの名無しさん
21/11/26 20:55:25.49 R0yJ4Kup.net
そんなに変わらへんやん、むしろuseで引き込む名前が増えて美しくないやん
174:デフォルトの名無しさん
21/11/26 21:02:27.39 Ye0bskEh.net
foo(true, false, true, false) や foo(None, None, None, Some(true)) みたいな呼び出しよりは100倍マシ
175:デフォルトの名無しさん
21/11/26 22:47:08.22 r6ugNRE0.net
ところでreadwrite()とかnewfile()ってなんですか?
176:デフォルトの名無しさん
21/11/27 00:09:21.90 riEP2Tv6.net
OpenOptionsの名称問題はともかく
なぜメソッドをチェーンさせているかというと理由は明白で
OpenOptionsExtトレイトをuseすると使えメソッドが拡張されて.mode()などが使えるようになるからだよ
177:デフォルトの名無しさん
21/11/27 00:15:29.99 4eTYjVC9.net
パイプ演算子は |> いつ実装されるの?
178:デフォルトの名無しさん
21/11/27 00:19:05.68 L+2AsAAt.net
どうせ上のtimes()トレイト君でしょ
179:デフォルトの名無しさん
21/11/27 00:42:49.16 w5eY7K13.net
チェーンの話なら、出来るだけチェーン派が多いけど.NETのLINQみたいになったら嫌?
それとも歓迎?LINQ形式のほうが分かりやすい。前の高階関数の話もそうだけど…
OpenOptionsはなんでもかんでもトレイトの弊害の気もする
articles =
from a in articles
orderby a.published descending
select new article_st;
URLリンク(github.com)
let e: Vec<i32> = linq!(from p in x.clone(), where p <= &5, orderby -p, select p * 2).collect();
このプロジェクトはcloneしてしまうところがダサいが、MSが参画しているということはありうるわけで
180:デフォルトの名無しさん
21/11/27 00:54:31.43 tWlgYd9Y.net
>>173
元がこうだもんな
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
そしてunix以外にも対応するために
std::os::unix::fs::OpenOptionsExtへ分けたのだろう
このメソッドチェーンビルダー方式以外のAPIでは詰む
181:デフォルトの名無しさん
21/11/27 05:50:15.37 IOkCeWcH.net
>>176
LINQはSQL風になる糖衣構文だからちょっと違うんちゃう。Rustに導入されても�
182:シの世界感と異質だし……。 個人的にはあれがSQL屋以外に直感的とは思えないw
183:デフォルトの名無しさん
21/11/27 09:55:26.18 sBo289Q7.net
>>174
rustは第一引数がselfの場合にメソッドのレシーバーになるのだから、実質パイプライン
184:デフォルトの名無しさん
21/11/27 11:01:40.71 kX7QbhiL.net
それはElixirと同じ「なんちゃってパイプライン」でメソッドチェーンが実現できるというだけでは。
ここで欲しいと言われているのは大元のF#のように自由関数やその部分適用が使えるパイプラインのことだろう。
185:デフォルトの名無しさん
21/11/27 12:26:31.88 Xdw8IP3R.net
自由関数?
186:デフォルトの名無しさん
21/11/27 14:14:35.41 A1VfIYPt.net
第一引数がselfとかじゃないやつのことじゃね
187:デフォルトの名無しさん
21/11/27 15:58:50.40 lc2cVbH3.net
自由関数ってなんだっけ?
自由変数ならわかるんだけど
188:デフォルトの名無しさん
21/11/27 15:59:45.03 Xdw8IP3R.net
定義を示してほしいわな
F#界隈にもRust界隈にも無い用語だし
189:デフォルトの名無しさん
21/11/27 16:36:46.11 kX7QbhiL.net
何気なく使ってたけど調べてみたらC++用語だったか。しかも日本じゃ「フリー関数」という表記の方が一般的なんだな。
メンバー関数(メソッド)ではない素の関数ということで。
190:デフォルトの名無しさん
21/11/27 17:08:07.51 FtAU5QYE.net
その意味ならElixirは自由関数も部分適用も使えると思うが・・・
191:デフォルトの名無しさん
21/11/27 17:20:05.91 kX7QbhiL.net
そっちか。
Elixirのパイプラインの使用例でよく見る形式が部分適用とは違うんで使えないものと思っていた。すまん。
部分適用は別の表記になるんだな。
192:デフォルトの名無しさん
21/11/27 17:59:12.10 g2vJAoph.net
C++でもフリー関数と言われたら free() のこと思い浮かべそうだが
193:デフォルトの名無しさん
21/11/27 18:21:56.04 molZfSEM.net
>>178
そうは言っても現状の下のこれが直感的に分かり易いとは思えないけど、、LINQはSQLとは関係ないよ。
whereとかselectがそれに見えるけど、言語的には直行性を高めた統合クエリなだけ
let mut y: Vec<i32> = x.clone().filter(|p| p <= &5).collect();
y.sort_by_key(|t| -t);
let y: Vec<i32> = y.into_iter().map(|t| t * 2).collect();
194:デフォルトの名無しさん
21/11/27 19:30:03.08 tWlgYd9Y.net
>>189
どこが見にくいのか具体的に教えて
use itertools::Itertools;
let y: Vec<i32> = x
.filter(|p| p <= &5)
.sorted_by_key(|t| -t)
.map(|t| t * 2)
.collect();
195:デフォルトの名無しさん
21/11/27 21:12:33.33 VfaT4D+K.net
>>185
> 何気なく使ってたけど調べてみたらC++用語だったか
どこの世界に非メンバ関数をフリー関数という馬鹿がいるのか詳しく
CかC++の規格のどこを参照すれば定義されているのか詳しく
196:デフォルトの名無しさん
21/11/27 21:19:49.42 w2+KtZN6.net
江添の馬鹿が言ってた。
URLリンク(cpplover.blogspot.com)
197:デフォルトの名無しさん
21/11/27 21:31:03.67 w2+KtZN6.net
Herb Sutterという馬鹿も使ってるな。
URLリンク(www.open-std.org)
198:デフォルトの名無しさん
21/11/27 21:39:57.32 VfaT4D+K.net
規格の上ではまだ見つけられないが
URLリンク(timsong-cpp.github.io)
member function
non-member function
という当然の名称が出てきている
日本語版の規格分かる人「フリー関数」とやらを引用してくだしあ
199:デフォルトの名無しさん
21/11/27 21:43:56.78 w2+KtZN6.net
規格で定義された用語というより業界用語的なもんでないの
URLリンク(github.com)
200:デフォルトの名無しさん
21/11/27 21:44:30.32 lc2cVbH3.net
馬鹿っていちいち言うなよ
素直になれや
201:デフォルトの名無しさん
21/11/27 22:07:33.35 tWlgYd9Y.net
その「フリー関数」とはクラスのメンバー関数ではない非メンバー関数として
クラスのないRustではその非メンバー関数の定義はどうなるの?
例えば以下のprint_all()は適当に作ったトレイトPrintAllのメンバーかもしれないけど
現実には('a'..='z').print_all();が動作してしまうわけで『誰のメンバー関数』なの?それとも非メンバー関数?
trait PrintAll<T> {
fn print_all(self);
}
impl<I: Iterator<Item=T>, T: Display> PrintAll<T> for I {
fn print_all(self: I) {
self.for_each(|x| println!("{}", x));
}
}
202:デフォルトの名無しさん
21/11/27 22:20:27.30 w2+KtZN6.net
第一引数がselfである関数(メソッド)を自由関数として呼び出すことはできるけどその逆は真ではないってことじゃないかな。
URLリンク(en.wikipedia.org)
203:デフォルトの名無しさん
21/11/27 22:24:30.93 dpgg2nfE.net
メソッド呼び出しできない関数は「フリー関数」ってことでええんちゃう?
非メンバ関数って呼ぶほうが断然一般的だとは思うが
204:デフォルトの名無しさん
21/11/27 22:47:33.66 riEP2Tv6.net
>>197
selfがIteratorだからクラス志向だとprint_all()はIteratorクラスのメンバーかな
しかしそのPrintAllはIteratorを継承してないから難しい
205:デフォルトの名無しさん
21/11/27 23:15:00.42 udgmz45E.net
URLリンク(dev.to)
この手のPhantomDataとジェネリクス使って特定の条件満たした状態じゃないとbuildとかのメソッドが実装されないようにしたりしたビルダーパターンってここの人たちはどう思ってる?
説明だけ見るとめっちゃ良いやん!って思うけどいざ自分で書くとメリットに対して無駄に複雑&使う側としてもエラーが見にくいとかなんか微妙に感じちゃう
206:デフォルトの名無しさん
21/11/28 00:27:08.53 j8Nrs0jp.net
>>200
例えばこのように任意の実装が出来るからprint_all()はIteratorのメンバーではなくあくまでもPrintAllのメンバー
struct V<T>(Vec<T>);
impl<T: Display> PrintAll<T> for V<T> {
fn print_all(self) {
self.0.into_iter().for_each(|x| println!("{}", x));
}
}
207:デフォルトの名無しさん
21/11/28 00:30:34.72 Fw4ypgsa.net
重要なのはフリー関数の定義じゃなくてパイプライン演算子の適用範囲だぞ
208:デフォルトの名無しさん
21/11/28 00:54:44.73 j8Nrs0jp.net
>>201
長くて斜め読みしかしていないがもっとわかりやすく示すとこういうことか
まず状態を示すダミーな型を作っておく
trait State {}
impl State for ToDo {}
impl State for Done {}
#[derive(Debug)]
struct ToDo;
#[derive(Debug)]
struct Done;
次にビルダーの構造体にダミーな型も収容する (サイズはゼロ)
type PD<T> = std::marker::PhantomData<T>;
#[derive(Debug)]
struct Builder<SetA: State, SetB: State> {
a: i32,
b: i32,
_a: PD<SetA>,
_b: PD<SetB>,
}
つまり変数aがセットされたか否かの状態をダミーな_aの型で示す
(つづく)
209:デフォルトの名無しさん
21/11/28 00:58:02.64 j8Nrs0jp.net
>>204の続き
初期値ToDoで開始してセットされたらDoneに変える
impl<SetA: State, SetB: State> Builder<SetA, SetB> {
fn new() -> Builder<ToDo, ToDo> {
Builder { a: 0, b: 0, _a: PD {}, _b: PD {}, }
}
fn set_a(self, a: i32) -> Builder<Done, SetB> {
Builder { a, b: self.b, _a: PD {}, _b: PD {}, }
}
fn set_b(self, b: i32) -> Builder<SetA, Done> {
Builder { a: self.a, b, _a: PD {}, _b: PD {}, }
}
}
全部がDoneになった時だけ実行可能にしておく
impl Builder<Done, Done> {
fn execute(&self) {
println!("OK: {:?}", self);
}
}
最初の呼び出しをわかりやすく用
fn new_builder() -> Builder<ToDo, ToDo> {
Builder::<ToDo, ToDo>::new()
}
あとは両方がセットされると実行できる
fn main() {
new_builder().set_a(123).set_b(456).execute();
}
片方でもセットを忘れるとコンパイル時にexecute()が解決できず失敗する
自分でこのコードを毎回間違えずに書くのは面倒なのでマクロ化されるなら採用
210:デフォルトの名無しさん
21/11/28 01:06:31.53 eXqoW6w6.net
またチラ裏コードの垂れ流し
いい加減やめてほしい
211:デフォルトの名無しさん
21/11/28 02:33:07.17 ZOlCZyFx.net
>>201
こういうのは手書きするのではなくて derive で良い感じに実装してほしい
あとrustdocの出力がごちゃつきそうなのが気になる
212:デフォルトの名無しさん
21/11/28 02:38:47.39 D4pWSHhU.net
自己満足コード見せられても困るよな
213:デフォルトの名無しさん
21/11/28 02:48:11.47 Fw4ypgsa.net
config部分が静的に決まってるならいいけど
UIやコマンドラインから動的にconfigしたい場合Builder traitを用意してtrait objectとして持ち回して
.execute()するためにdowncastする必要が……とかで二度手間になりそう
214:デフォルトの名無しさん
21/11/28 02:53:52.09 ghhRE59c.net
>>201のコードがいいと感じる感性が理解できないよ
215:デフォルトの名無しさん
21/11/28 10:39:31.01 XbegH2kB.net
>>201
難しい話をすると
ビルダはディレクタに対して抽象化されており
一個のディレクタが複数のビルダをケアできることを考えると
必須パラメータというのをビルダ実装ごとに準備するなら
ビルダというインタフェースがディレクタに対して実質破綻してると思う
でも
ビルダインタフェースが正しい呼び出し順を想定してたりするのをアリとするなら
必須パラメータも同じように勝手に想定しておいて
使う側にはドキュメントなりなんなりで勝手に指示だしとけば十分とも思う
そんで
呼び出し順や必須か否かに想定を置きたくない
呼び出し側に完全な自由度を与えたビルダインタフェースを提供したいなら
インタフェース Fooビルダ {Fooビルダ a(); Fooビルダ b();}
クラス 実際のビルダ implements Fooビルダ {
Foo(必須な何か x) {略} // コンストラクタで与えるとか
Fooビルダ 必須なc() {略} /* 実際のビルダ固有のメソッドとして与えて
new 実際のビルダ().必須なc().a() などと呼ぶか
Fooビルダ a() {略}
Fooビルダ b() {略}
Foo create() {略}
}
のようにして、ビルダインタフェース側だけはクリーンに守っておけばスッキリかも?
元のURLにあるように何が必須かをパラメータ化したいという欲求は解消してないのは認める
216:デフォルトの名無しさん
21/11/28 11:48:41.93 sDAG0wCq.net
インタフェースとかクラスとか意味不明すぎ
217:デフォルトの名無しさん
21/11/28 23:35:20.49 Fzo1fdIE.net
ここまで意味不明な文章書けるのって逆に凄いよ
このレベルは久々に見たわ
218:デフォルトの名無しさん
21/11/29 01:53:08.06 zo5XubVi.net
Javaコードの識別子の部分を日本語で書いて
GoFのオリジナルのBuilderパターンを説明しただけっぽい
なんでRustスレでそんな話をしたのかだけは完全に謎だが……
219:デフォルトの名無しさん
21/11/29 02:06:35.83 27e/xIh/.net
普通のBuilderであまり困ったことないからなあ
操作によって遷移していく状態があって、状態ごとに可能な操作が違う(呼び出せるメソッドが違う)とかなら意味あると思う(Socketのbind->accept->read/writeみたいな)
ただこういうものはもはやBuilderと呼ぶべきものではないと思う
220:デフォルトの名無しさん
21/11/29 09:45:31.17 3xWgo/Oc.net
それはStateパターンでしょ
221:デフォルトの名無しさん
21/11/29 15:25:28.77 aqqw7fQi.net
URLリンク(play.rust-lang.org)
sもmutにしてDerefMutもimplしてるのになんでgの方は通らないんでしょうか?
222:デフォルトの名無しさん
21/11/29 16:49:12.44 4MgUQE5v.net
>>217
もっと単純に let z: *mut i8 = *s; も通らないので
これはDerefMutの条件であるmutable contextを満たしてないのではないか
そしてもちろん let z: &mut i8 = *s; は通るし **s = 88; も通るからDerefMut自体は機能している
223:デフォルトの名無しさん
21/11/29 18:03:30.09 3r4jR24z.net
PR出てるけどどうなるかわからない
URLリンク(github.com)
現状は
let s = &mut *s;
g(*s);
するか
g(&mut **s);
するか
224:デフォルトの名無しさん
21/11/29 18:58:43.82 Y4F1AeLY.net
>>219
おお、そのものズバリが
あざます!
225:デフォルトの名無しさん
21/11/30 18:31:42.77 8WvE/rry.net
ifの閉じカッコにセミコロンが必要となる条件を教えてください
以下のプログラムはそれが足りないと指摘されてコンパイルエラーとなり
セミコロンを付けると通って動作するのですがどういう原理なのでしょうか?
fn main() {
for line in BufReader::new(io::stdin()).lines() {
let line = line.unwrap();
if let [first, second, rest] = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>()[..] {
println!("{}:{}:{}", first, second, rest);
}
}
}
226:デフォルトの名無しさん
21/11/30 18:58:54.86 ZW4IpnTF.net
戻り値?
227:デフォルトの名無しさん
21/11/30 20:27:35.13 s7fhQ2Tk.net
ブロック式最後の式の値は、ブロック式の値として返るので drop されるタイミングがブロックの末尾よりも後の箇所になるということかな
for のブロックは値を返さないけど、ブロック式と同じ扱いをされているっぽい
URLリンク(play.rust-lang.org)
228:デフォルトの名無しさん
21/11/30 21:12:32.45 8WvE/rry.net
>>223
>> ブロック式最後の式の値は、ブロック式の値として返るので
もちろんおっしゃる通りでその例でも>>221の例でもif式の値は明瞭に()ですね
>> drop されるタイミングがブロックの末尾よりも後の箇所になるということかな
ブロック式の値として返るのは()ですから変数lineのdropタイミングが後にはならないように思うのですがどうなのでしょう?
229:デフォルトの名無しさん
21/11/30 21:32:53.10 hx6pGpzB.net
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
↑エラーメッセージに理由書いてるよ
230:デフォルトの名無しさん
21/11/30 21:55:20.40 8WvE/rry.net
>>225
そのエラーメッセージでは理由になっていないのではないでしょうか?
例えば(for文を使わない)>>223の以下の部分を
if let [_first, _second, _rest] = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>()[..] {}
このように2行へ書き換えても同じくセミコロンを付けろエラーとなりますが
let x = line.splitn(3, ' ');
if let [_first, _second, _rest] = x.collect::<ArrayVec<_, 3>>()[..] {}
このように2行へ書き換えるとコンパイルが通ります
let x = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>();
if let [_first, _second, _rest] = x[..] {}
この差をどう見るのか教えていただけますでしょうか
231:デフォルトの名無しさん
21/11/30 22:02:05.25 s7fhQ2Tk.net
>>224
・lineの寿命はif-let式の寿命と一致する
・ブロックの最後の文の寿命はブロックを囲む文の寿命と一致する
という仕様になっていると思われる
型は関係なくてブロック内の最後の式か否かが重要
232:デフォルトの名無しさん
21/11/30 22:26:55.08 hx6pGpzB.net
>>226
>このように2行へ書き換えるとコンパイルが通ります
>let x = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>();
line.splitnでborrowしたものを使ってるtemporaryは
let xで受けたタイミングでtemporaryじゃなくなる
(エラーメッセージにあるdropped soonerの状態になってる)
if式がブロックの最後の式でそのif式の一部がborrowを使ってるtemporary
セミコロンがないとtemporaryが解放される前にローカル変数のlineが解放されるからライフタイムのエラー
そのうち修正されるかもしれないけど今はそういう動きってこと
233:デフォルトの名無しさん
21/11/30 22:31:28.50 hx6pGpzB.net
>>226
>let x = line.splitn(3, ' ');
>if let [_first, _second, _rest] = x.collect::<ArrayVec<_, 3>>()[..] {}
234:デフォルトの名無しさん
21/11/30 22:35:06.87 hx6pGpzB.net
すまん、投稿ミス。
値を返したいときはセミコロンを追加するだけじゃだめだから
一旦変数で受けてからその変数をブロックの最後に書く
235:デフォルトの名無しさん
21/11/30 22:39:09.24 8WvE/rry.net
ふむむ
lineがヒープにあると以下の2行だけでセミコロン付けろエラーになりますね
let line = String::from("A B C D E");
if let [_first, _second, _rest] = line.splitn(3, ' ').collect::<ArrayVec<_, 3>>()[..] {}
このif let文を例えば以下のように変えるとセミコロンを付けなくても通るのはどうしてでしょう?
if let Some(_s) = line.get(3..6) {}
236:デフォルトの名無しさん
21/11/30 22:41:34.40 rIKeeiBO.net
なんでセミコロン付けると一時オブジェクトの寿命変わるんだって思ったけどそういえばC++にもそんな仕様あったな……
完全式じゃなくて戻り値に使うから破棄されないってことか
237:デフォルトの名無しさん
21/11/30 23:55:05.80 Y6JwF3m3.net
>>230
ブロックの最後で返す値は変数に入れなくても複雑な式でも大丈夫ですよ
238:デフォルトの名無しさん
21/12/01 12:41:12.28 KNT1MQhQ.net
なんでBufReadトレイトはあるのにBufWriteトレイトは無いんですか?
239:デフォルトの名無しさん
21/12/01 13:25:08.66 2cjkBPbo.net
>>231
詳しく知りたければまずはここ↓だけど
URLリンク(doc.rust-lang.org)
言語自体をいじりたいわけじゃなければ
あんまり深追いしても役に立たないよ
240:デフォルトの名無しさん
21/12/01 16:06:22.55 oI4zTDt2.net
>>234
BufReadはReadに対して追加の機能(行単位の読み込みなど)があるけど
Writeに対して追加すべき機能がないからBufWriteは存在しないのではないかと思われる
241:デフォルトの名無しさん
21/12/01 21:58:24.00 QwFnQ8Qa.net
flushしなけりゃ下のレイヤのどこかで勝手にバッファリングされることがほとんどだから用意する意味がないんだろな。
242:デフォルトの名無しさん
21/12/01 23:34:44.63 hYYowF9a.net
>>237
いやBufWriteトレイトが存在しないだけであってBufWriterはちゃんとある
そしてBufWriterがバッファリングする最後の要
誰かが勝手にバッファリングしてくれることはない
例えば何行もファイルに書き込む時にBufWriter使わずに各行毎にwriteしてたら遅くなる
243:デフォルトの名無しさん
21/12/03 14:04:46.92 xjf4hyRh.net
URLリンク(crates.io)
とか
URLリンク(crates.io)
みたいなの使ってる人おる?
これ使うくらいならLL使うとは思うが
244:デフォルトの名無しさん
21/12/03 14:45:17.46 RidNMi7I.net
REPL的にコードスニペットの実行確認するために使ったりはできるのかな
今はplaygroundで事足りているが
245:デフォルトの名無しさん
21/12/04 15:22:12.31 f29/w5s1.net
Read::read_to_end()に空のVecを渡した時に戻り値のusizeとVecのlen()が違う値になる事って有り得ますか?
246:デフォルトの名無しさん
21/12/04 23:32:14.41 VZLpyp2B.net
同じと思う
let file_size = file.metadata().map(|m| m.len())?;
let file_pos_before = file.stream_position()?;
let read_buf_size_before = read_buf.len();
このようなfileとread_bufがある任意の状況で
let read_size = file.read_to_end(&mut read_buf)?;
とread_to_end()すると以下が常に成り立っていると思われる
assert_eq!(read_buf_size_before + read_size, read_buf.len());
assert_eq!(file_pos_before + read_size as u64, file_size);
247:デフォルトの名無しさん
21/12/05 23:43:02.39 +2NbegRW.net
> loop式はbreakで指定した値を返せるのに
> なぜwhile式やfor式は値を返せないの?
> Option型にしてbreakで値を指定した時だけSome(値)としてそれ以外はNoneとすれば便利なのに
そのloopと組み合わせればよい
(例)
let r = 'result: loop {
for x in 1..100 {
for y in 1..100 {
if x * y > 1234 {
break 'result Some((x, y));
}
}
}
break 'result None;
};
assert_eq!(r, Some((13, 95)));
248:デフォルトの名無しさん
21/12/06 12:54:15.14 BduPW1Ae.net
>>243
forよりイテレータを繋げた方が単純でわかりやすいよ!と言おうとしたら
let r = (1..100)
.map(|x| (1..100)
.map(|y| (x, y))
.find(|(x, y)| x * y > 1234)
)
.find(|o| o.is_some())
.map(|oo| oo.unwrap());
assert_eq!(r, Some((13, 95)));
むしろ複雑でわかりにくくなってしまったw
まさかの>>243のダミーloop使用がベストアンサーなのか!?
249:デフォルトの名無しさん
21/12/06 13:12:38.43 PE/XVSQC.net
>>244
flat_mapを使えば良い
let r = (1..100).flat_map(¦x¦ (1..100).map(¦y¦ (x, y))).find(¦(x, y)¦ x * y > 1234);
250:デフォルトの名無しさん
21/12/06 13:28:57.99 GjZweGXf.net
どれもこれも分かりやすいとは思えない
251:デフォルトの名無しさん
21/12/06 15:59:22.06 BduPW1Ae.net
結局見やすくこう書けるといいんだよね
let r = pair(1..100, 1..100).find(|(x, y)| x * y > 1234);
assert_eq!(r, Some((13, 95)));
これでassertも通ったけどyのイテレータとx自身の2ヶ所にCloneが必要となってしまった
避けられないような気がするけどどうでしょうか?
fn pair<IX: Iterator<Item=X>, IY: Iterator<Item=Y> + Clone, X, Y>(ix: IX, iy: IY) -> Pair<IX, IY, X, Y> {
Pair { cur_ox: None, cur_iy: iy.clone(), ix: ix, iy: iy, }
}
struct Pair<IX: Iterator<Item=X>, IY: Iterator<Item=Y>, X, Y> {
cur_ox: Option<X>, cur_iy: IY, ix: IX, iy: IY,
}
impl<IX: Iterator<Item=X>, IY: Iterator<Item=Y> + Clone, X: Clone, Y> Iterator for Pair<IX, IY, X, Y> {
type Item = (X, Y);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let None = self.cur_ox {
self.cur_ox = self.ix.next();
}
if let Some(ref x) = self.cur_ox {
if let Some(y) = self.cur_iy.next() {
break Some((x.clone(), y));
} else {
self.cur_ox = None;
self.cur_iy = self.iy.clone();
continue;
}
} else {
break None;
}
}
}
}
252:デフォルトの名無しさん
21/12/06 16:01:51.04 NQsvo9rr.net
最近知ったボクの大発見書いていい?
let a: Option<i32> = None;
これは
let a = None::<i32>;
と書ける
関数のパラメータとして渡そうとして
f(None)で怒られたとき
f(None::<i32>)として怒られない
しょうもないレス失礼いたしました
253:デフォルトの名無しさん
21/12/06 16:21:14.38 BduPW1Ae.net
>>248
曖昧性を確定させる::<型>の指定は色々なところで出てくるね
例えばcollect::<Vec<_>>()とか
ブロックやクロージャの返り値でOk::<(),std::io::Error>(())とか
254:デフォルトの名無しさん
21/12/06 16:35:36.71 McsJgKJD.net
>>247
itertoolsのcartesian_productがほぼそれ
255:デフォルトの名無しさん
21/12/06 17:07:16.47 +ZC47hZJ.net
iproduct!
256:デフォルトの名無しさん
21/12/06 17:09:45.91 Fu08U5Ef.net
>>248
型パラメータは Option のパラメータなので順当に考えれば Option::<i32>::None と書くべきだし実際にそれで通るんだけども、
歴史的経緯でバリアントにも付けられるようになってる。
短く書けるから習慣的にはバリアントに型を付けるほうが多いかな……?
257:デフォルトの名無しさん
21/12/06 17:31:38.25 BduPW1Ae.net
>>250
なるほど直積集合かぁ
同じくyのイテレータとxはCloneを要求してますね
fn cartesian_product<J>(self, other: J) -> Producfgt<Self, J::IntoIter>
where
Self: Sized,
Self::Item: Clone,
J: IntoIterator,
J::IntoIter: Clone,
258:デフォルトの名無しさん
21/12/06 20:06:32.13 NQsvo9rr.net
>>249
> Ok::<(),std::io::Error>(())とか
正直見たことなかったです
>>252
> 歴史的経緯でバリアントにも付けられるようになってる。
(´・∀・`)ヘー
そういうわけなんですね
259:デフォルトの名無しさん
21/12/06 21:47:08.33 BduPW1Ae.net
>>254
例えばlet x = spawn(async { ... });して裏で何か処理をやらせといた結果を
後でもしエラーが出ていたら進めちゃいけないタイミングでx.await?;で確認するわけだけど
spawnが返す型をxに律儀に記述するのは面倒なのでそこは略すとして
asyncブロック内で?とOk(())だけ書くとコンパイラが文句を言うので仕方なく記載
260:デフォルトの名無しさん
21/12/06 22:11:44.75 NQsvo9rr.net
なるほど勉強になりました
261:デフォルトの名無しさん
21/12/07 21:26:04.18 pFZAiCY5.net
12次元までならiproduct!が使える
use itertools::iproduct;
let r = iproduct!(1..100, 1..100).find(|(x, y)| x * y > 1234);
let r = (|| {
for (x, y) in iproduct!(1..100, 1..100) {
if x * y > 1234 {
return Some((x, y));
}
}
return None;
})();
262:デフォルトの名無しさん
21/12/08 14:18:48.01 6NuoEm2L.net
ARM版のWindowsでRustのコードを書くのってめんどくさいな
生のWindowsで使用する場合、VisualStudioに付属するx86用のリンカーが非推奨警告を無視すれば使えたものの、次期バージョンからx64専用になってしまうっぽい
一方、仮想環境等+VSCodeでは、RustAnalyzer等が機能せず苦労する・・・・
持ち運び用にケチってMacを買わなかったのが大問題だった、なんかいい方法ないのかな?そもそもARM Windowsで動くリンカーってVisualStudio付属のものしかないのかな?
ケチったって言ってもよくよく考えてみると大して金が浮いてもないし、変なもん買っちまった
263:デフォルトの名無しさん
21/12/08 14:36:22.94 W5vI+s6z.net
>>258
i686ターゲットのバイナリをバイナリ変換で動かしてるってこと?
aarch64-windowsターゲットとARM用のリンカ使えばネイティブのARMバイナリできるんじゃないかと思うけど
264:デフォルトの名無しさん
21/12/08 20:15:10.58 pzF9gjPk.net
バイナリバイナリルルルルルー。
265:デフォルトの名無しさん
21/12/08 21:25:01.32 t4Pzut9P.net
rustはどんどん新しい機能が追加されていくのはいいけど、
後方互換性を気にして過去に追加された「間違った」機能を削除するという
思い切ったこともやって欲しいな
これまでの言語は後方互換性にとらわれて滅茶苦茶になってるから
266:デフォルトの名無しさん
21/12/08 21:29:04.83 ff6DaDGr.net
>>261
C++かな?
267:デフォルトの名無しさん
21/12/08 21:44:15.05 tBq4QMAR.net
>>261
editionでは物足りない?
268:デフォルトの名無しさん
21/12/08 21:50:49.56 t8qOOWvR.net
実際2021editionではレンジパターンの ... が削除されて ..= に一本化された
269:デフォルトの名無しさん
21/12/09 08:53:14.94 sqaPNXyj.net
>>258
今どきDocker使うのはデフォだから何の問題もない、因みにRustAnalyzerも使える
使えないのは、復数のプロジェクトが見える状態にしてるから
今どき復数ウィンドウ立ち上げても何の問題もない、ルートフォルダを固有にしたら解決
つまり、調査不足が原因
270:デフォルトの名無しさん
21/12/09 10:59:17.60 4q0mFQ+L.net
ARM は安物のイメージがある。
WSL2, Linux, Docker, VSCode とか使えるのかな?
Mac も、M1 に変わったから
271:デフォルトの名無しさん
21/12/09 11:10:00.09 XwSSuf4e.net
原理的には ARM �
272:フほうがインテル系 (もはや AMD 系と呼ぶべきか) よりも高速化できる可能性があるとは言われている。 今以上に回路を細かくするのは無理というところまできてしまっているのでアーキテクチャのほうで見直しが必要なんだが インテル系は互換性が足かせになってしまっていてあまり思い切ったことが出来ない。 現時点はインテル系向けにチューニングされたソフトウェア資産が多いから上手くいっているけど将来もそうとは限らない。
273:デフォルトの名無しさん
21/12/09 11:45:18.26 t4DQqTrM.net
>>267
スレチだけどその話の出展あったら教えてほしい
274:デフォルトの名無しさん
21/12/09 14:22:31.39 RSXecyhf.net
CISCRISCの話じゃなくてメモリモデルの話ならそうかなって思う
今のx64って実質中身RISCって聞いたことあるし
Rustで言うとx64では全部SeqCstとして扱われるみたいな話
275:デフォルトの名無しさん
21/12/09 17:42:03.96 VJ9QB09P.net
リソースの話だけなら命令デコーダーとか?
x86_64は拡張や互換性とか可変長命令だったりで無駄にトランジスタ消費するのが電力性能比的に足かせみたいな
276:デフォルトの名無しさん
21/12/09 20:39:08.00 0MvTGuxY.net
今どきのプロセッサだと分岐予測器やキャッシュが支配的でデコーダなんて誤差だと思う
あと可変長は命令側の帯域やキャッシュ効率が良くなるという面もあってRISC系でも採用してることが多い
結局両者とも長年の改善で似たようなとこに落ち着いてるわけで、ISAが違うからどうこうみたいな話は結構眉唾
277:デフォルトの名無しさん
21/12/11 19:08:58.23 oic9EtmK.net
こういうことをしたいのですがコンパイルエラーとなってしまいます
const DEFAULT_NAME: &str = "namae";
let arg1: Option<String> = std::env::args().nth(1);
let name: &str = arg1.map_or(DEFAULT_NAME, |s| s.as_str());
どう直せばよいでしょうか?
278:デフォルトの名無しさん
21/12/11 19:10:55.89 ZSpAs+oG.net
namaeじゃなくてnameな
aが余計
この程度の英語のスペル書けないとかプログラミング向いていないしやめた方がいいよ
279:デフォルトの名無しさん
21/12/11 19:21:25.94 UNEoSQah.net
自演ボケか?
280:デフォルトの名無しさん
21/12/11 19:35:01.57 K+rsGRUk.net
>>272
そのままだとarg1が消費されてなくなるのに参照だけ残ることになるからエラー
Stringで受ければいいよ
let name: String = arg1.unwrap_or(DEFAULT_NAME.to_string());
281:デフォルトの名無しさん
21/12/11 20:19:05.99 XRkKLs6o.net
>>273
すげーな。してはいけないレビューの典型例じゃねーか。
すげーな