Rust part13

■ このスレッドは過去ログ倉庫に格納されています
2021/11/07(日) 10:04:59.35ID:pJhT3MIE
公式
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の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 part12
https://mevius.5ch.net/test/read.cgi/tech/1629813327/
2021/11/07(日) 13:03:26.24ID:GwJCd0Qm
ある値をちょっとだけ処理したものにどんな名前を付けたいかってのは、
まあ適した名前があるならそれに越したことは無いけど
同じ名前を付けることによってたいした違いはない、かつ以前の値にアクセスすることはもうないことが明示できるので
それはそれで使いようはあるよ。

以前の値にアクセスしないという意思を持ってシャドーイングを使うのでそれで困ることはない。
うっかりで同じ名前を付けてしまった場合も大抵の場合は型の違いとかで検出されるし。
7デフォルトの名無しさん
垢版 |
2021/11/07(日) 14:26:48.84ID:c7aT0NV0
何を問題にしてるのか分からんけど、コンパイラから見たら、同じ変数名宣言でも連番で構文解析しているわけで
ブロックスコープによりシャドーされても何ら関係ないが、インライン展開して最適化するrustコードだと問題が
出る場合もありうる。それとシャドーと以前の値にアクセスすることはもうない事は意味が違う
2021/11/07(日) 14:35:43.57ID:GwJCd0Qm
>>7
間接的にアクセスしうるとかいうのはもちろんあるけど、
あくまでもプログラマが読み書きする上での意図として明示するという意味ね。
実際にもう (直接には) 使えないんだから使えないという意味だよ。
9デフォルトの名無しさん
垢版 |
2021/11/07(日) 14:45:10.83ID:K5DEbKWG
ん?まあありうるだろうけど、そんな意図を持ってシャドーイングをするコードは捨てろよ?明示じゃねーわ
2021/11/07(日) 15:07:42.51ID:GwJCd0Qm
そうか。
11デフォルトの名無しさん
垢版 |
2021/11/07(日) 15:10:45.19ID:XJB+ymj6
>>996
C/C++だとスコープ中で外のスコープの宣言と被ってたら警告出るよね
2021/11/07(日) 15:27:52.97ID:BjoZRpKT
>>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デフォルトの名無しさん
垢版 |
2021/11/07(日) 16:09:12.89ID:XJB+ymj6
おま環
2021/11/07(日) 16:58:36.83ID:NoqiBvXu
intellijでも、シャドーイングがあったときは警告出てなかったっけ?
15デフォルトの名無しさん
垢版 |
2021/11/07(日) 17:10:25.95ID:qLFsTeDp
変数名にシャドーイングって含めとけばいいのでは
16デフォルトの名無しさん
垢版 |
2021/11/07(日) 17:23:30.13ID:BGBI+61D
そんなことよりもだ!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);
2021/11/07(日) 17:27:45.44ID:Vt90/0HU
foldじゃなくてsumのほうが良いのでは
18デフォルトの名無しさん
垢版 |
2021/11/07(日) 17:29:32.83ID:TOVMzjUd
let acc1: u32 = (0..).map(|n| n * n)
 .take_while(|&n_squared| n_squared < upper)
 .filter(is_odd)
 .sum()
19991
垢版 |
2021/11/07(日) 17:45:31.24ID:tLg/y1Lc
>>5
不意のミスでも結構コンパイルで引っかかってくれるようで安心しました。Rustさんよく考えられてますね。知りたかったことが書いてありました。ご丁寧にありがとうございます
2021/11/07(日) 18:44:40.25ID:jMCdC4Py
shadowing嫌いな人はdeny(clippy::shadow_same)などすればよい
21デフォルトの名無しさん
垢版 |
2021/11/07(日) 18:52:44.84ID:UxYGnxuj
>>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に
比べ意図が明確になりますが、それは自己満足の幅が大きいとも言えます
2021/11/07(日) 19:20:41.26ID:AmV/cRIg
>>18のコードは大きな配列とかメモリ大量消費とかしないだろ
小さなstructを以下の4つ消費するのみで誤差
RangeFrom, Map, TakeWhile, Filter
23デフォルトの名無しさん
垢版 |
2021/11/07(日) 19:35:16.03ID:UxYGnxuj
>>22
18のコードは配列ではなくイテレーターなのでそうですが、map,take_while,filterのほうが
好みの人が多いとは思いますが、私が言いたいのは何でもかんでもHigh orders functionに
して書くのはよく知らないと、リスクがあるということとブレークポイントなどを仕掛けて
経過は見れないということです。日本語が読めない人はもう少し考えましょう
2021/11/07(日) 19:40:56.86ID:BxmvbDqp
メソッドチェーンなら、メソッドごとに単体テストができるし、テストを用意しとけばすぐにバグがわかるような気がするが。
25デフォルトの名無しさん
垢版 |
2021/11/07(日) 21:07:08.18ID:UxYGnxuj
ほんと日本語読めない奴ばっかり。経過いうてるのにメソッド毎だとか、バグのことなんて言って
ないのに(特殊な例を言えば配列の場合でmap().map()などとしたメモリー使用量)を言っている
のに、そもそも個人的には言うてんのに、そんなに説得したいのか?
2021/11/07(日) 21:12:13.09ID:0IMrdMn2
翻訳は、Chrome, Edge の翻訳機能、

Google の翻訳サイトとか、DeepL とか
2021/11/07(日) 21:12:13.85ID:t/1xwUA+
経過も見れるよ
28デフォルトの名無しさん
垢版 |
2021/11/07(日) 21:20:10.03ID:UxYGnxuj
言語は素晴らしいのにやってる奴が意識高い系のウンコ野郎ばっかり
29デフォルトの名無しさん
垢版 |
2021/11/07(日) 21:27:42.49ID:aMDc0Kvs
まあコミュニティに馴染めるかどうかも本人の適性によるところがあるしな
2021/11/07(日) 22:21:32.27ID:BjoZRpKT
>>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のメソッドチェーンを使うかの話に配列は一切関係ない
2021/11/07(日) 22:27:15.28ID:jMCdC4Py
配列とスライスとベクタが別物なのこういう場所で会話するときに地味に混乱しがち
2021/11/07(日) 22:28:05.16ID:jMCdC4Py
Stringとstrって日本語でどう呼び分けるんですか?
33デフォルトの名無しさん
垢版 |
2021/11/08(月) 07:17:46.35ID:59xs5iEI
inspectで経過見れるじゃん
34デフォルトの名無しさん
垢版 |
2021/11/08(月) 07:29:23.24ID:n6XCACTA
>>30
こういう内容ゼロの事を言ってる意識高い系が普及を邪魔してる。自身が”何度も使えば配列地獄になるけど”と
認めてるにも関わらず、最初からイテレータを使えばと先に言ってることをそのまま返す
何が”したがって”なのか全然分からんが、説明した気になり、”チェーンを使うかの話に配列は関係ない”と
まとめる。そもそもチェーンを批判なんてしてない(対象によりけり高階関数の連続使用はメモリーを圧迫する
可能性があるということだけ)それとも言い負かしたいだけの気持ち悪さをそんなに使用者に伝えたいのか?
2021/11/08(月) 07:59:01.69ID:SITQ70se
>>25
君が二つの全く異なるmap()をゴチャ混ぜにして皆を騙して混乱させているだけだ
我々はfor文で回すかイテレータをチェーンするかの話をしている
君が持ち出した[T; N]::map()は配列を配列に変換するのみでイテレータと一切関係ない
我々はmap()に関しては一貫してIterator::map()の話しかしていない
36デフォルトの名無しさん
垢版 |
2021/11/08(月) 08:32:39.74ID:fuBvo+92
ほんとここに来るやつは皆を騙す詐欺師の気持ちや悪い奴ばかり、一切関係ないぞ!
2021/11/08(月) 10:16:54.73ID:csyRALBm
どんなに適当に書いてもコンパイラさんがうまいこと最適化してくれるのでヨシ!
38デフォルトの名無しさん
垢版 |
2021/11/08(月) 10:32:52.09ID:xhkp7neH
最初から大して違いも出ないが、分かりやすさを重視するなら高階関数かな?
これはこう書け!と口煩くいってくる奴が理解できないけど…
でも裾野を広げるためには未熟で冗長なコードでも許容しなきゃね、1つもfor無しで
デカいプロジェクトを書くことなんて無いし、趣味でやってるだけなら縛りもありだが
2021/11/08(月) 10:39:48.75ID:XWIn4U6b
高階関数でかくと並列化しやすいからそう書いてる
2021/11/08(月) 11:16:30.19ID:NiBBbin2
>>39
rayonなどに移行しやすいという意味?
41デフォルトの名無しさん
垢版 |
2021/11/08(月) 11:23:59.38ID:OCI2yhfb
rustの得意とする高パフォーマンス領域だとactix(-web)なんかはスレッド数=物理コア数で
下手に並列化しないほうがrequest/secは高いから、標準の高階関数も並列にはなっていない
訳で、バッチ処理みたいなその時に他の処理が走ってなくて、如何に処理時間を短くするか
というプログラムなら最初からRayonとか最初に導入してそう…
状態共有しない並列起動だけなら、thread::spawn(|| { for i in 0..x {...} }でも比較的に
簡単に出来るが、そういう意味では無いんだろう
2021/11/08(月) 11:28:09.63ID:hB1EvZ1K
さすが長文w
中身がナッシング
2021/11/08(月) 12:29:17.17ID:QzkUvy+x
>皆を騙す詐欺師の気持ち悪い奴ばかり

都合悪くなると話題変えて逃げるしな
2021/11/08(月) 12:33:26.36ID:4QnDXIIG
async/awaitが高階関数と(今の所)相性良くない
45デフォルトの名無しさん
垢版 |
2021/11/08(月) 12:45:54.28ID:ODzJsaPg
話題変えて逃げてんじゃねーよ!カス!
2021/11/08(月) 13:17:11.03ID:NiBBbin2
>>44
今のところとしてるのは改善策が取り込まれる見込みだから?
それとも単に将来のことはわからない程度の意味?
2021/11/08(月) 13:48:44.02ID:vASCcAjA
静的チェックでオールOKみたいな言語って意識高い系のバカを呼び寄せやすいんだよ。
奴らは動的テストなんてしたくないって意識をすぐ誤魔化す癖がついてる。
2021/11/08(月) 14:48:38.73ID:xhY6YpeY
ストローマンがあらわれた
2021/11/08(月) 14:51:34.92ID:vASCcAjA
自分のことを言われて怒っちゃったのか。
わかりやすいね。
2021/11/08(月) 14:53:05.69ID:M7ed0MM2
>>32
ストリングとストラ
もしくは
ストリングとストリングスライス
51デフォルトの名無しさん
垢版 |
2021/11/08(月) 15:29:22.26ID:GGqoyJW1
うん、完全理解した。こんスレは同じrustガンダム乗りなのに連邦の白い悪魔以外は認めないんだ
どっちでもいいじゃんか・・・
https://i.imgur.com/a9pC9PS.jpg
2021/11/08(月) 17:38:09.25ID:7WV3o+af
Vecに入ったi32(空っぽの可能性もあり)の平均を計算するのってどうするのが一番スマート?
53デフォルトの名無しさん
垢版 |
2021/11/08(月) 17:43:46.40ID:Chf4/Vgn
高階関数使う
2021/11/08(月) 20:19:03.35ID:QIO9hdEn
副作用持つときはfor文でそれ以外はメソッドチェーン繋げてやります
2021/11/08(月) 21:14:06.85ID:6BzlB5w0
>>34
イテレータの高階関数の連続使用でメモリーを圧迫することはないと思います
各イテレータ自体はコンパクトな構造体ですし高階関数自体の処理も基本的にはメモリーを圧迫するものではありません
ただし全ての要素を受け取らないと算出できない物もあるため一時的に長いVecを使うことになるイテレータもあります
この場合はイテレータを用いずに自分でforなどを回す方法をとっても同様にして一時的に長いVecを使うことになるでしょう
2021/11/08(月) 21:14:39.00ID:IC/lLBzS
そういや平均求めるメソッドってstd::iterにもitertoolsにもないんか?
2021/11/08(月) 21:38:29.05ID:NiBBbin2
>>47
Rustには動的テストを容易にする仕組みが入ってるしRustの話ではなさそうだ
2021/11/08(月) 21:38:34.43ID:3AO0oG+L
そんな簡単な実装を一々stdに入れてたら、糞関数だらけのLLライクになるじゃん?
2021/11/09(火) 04:01:57.70ID:d6arxLIn
>>58
std::iterに入れろとまでは思わないけど、
sumもcountもイテレータ消費しちゃうから微妙にめんどくさくない?
2021/11/09(火) 20:24:06.75ID:E/uEHC7F
>>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());
}
2021/11/09(火) 22:53:03.40ID:yHbe3Xjs
オーバーフローしないように型を選択することとか、
汎用的に使えて問題を避けることを考えたら平均を計算するのってそんなに簡単な話ではないと思う。
62デフォルトの名無しさん
垢版 |
2021/11/10(水) 08:15:17.85ID:BLadVH6y
stats::median
今でもfilter使えだとかうっせーのばっかり、標準ライブラリは小さくて良いYO
https://lib.rs/science/math
statrs-dev/statrsとか好きなの引っ張ってきて入れろよ?平均じゃなくてもさ
2021/11/12(金) 17:23:46.15ID:pYLNCtl5
Vecとかに入った複数のtokio::task::JoinHandleをjoinするのはどうやるんでしょうか?
2021/11/12(金) 20:00:37.76ID:PXROpwUc
thread::JoinHandleは並列で(マルチスレッドとなり)
join()で待つ

task::JoinHandleは非同期で(シングルスレッド時は並行、マルチスレッド時は並列となり)
awaitで待つ
2021/11/12(金) 21:00:47.58ID:CuMBVxkM
futures::future::join_allでいいんじゃね
2021/11/12(金) 23:59:03.69ID:BY1dkyYH
task::JoinHandleの方はFuture trait実装なので
列挙ならfutures::join!(f1,f2,f3,...)マクロ
VecならIntoIteratorに対応のjoin_all(v) >>65
2021/11/17(水) 00:04:41.47ID:6Xk/Jjaa
おいウンコ引き取りに来いよ
2021/11/20(土) 19:33:51.68ID:i03LC++E
let mut a = 'a';
let mut d = 'd';

match 'b' {
 a..=d => { println!("true") }
 _ => { println!("false") }
}

matchでcharの範囲指定ができるからって試したんだけど、
変数に入れたcharだと範囲指定ってできないの?
これだとあんまり意味ないような
2021/11/20(土) 20:32:13.75ID:iQUqB4pW
パターンには代入される側の変数しか書けないけどガードは自由自在
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);
}
2021/11/20(土) 20:43:54.76ID:iQUqB4pW
そして範囲指定を使いたいんだったらこう
  _ if (a..=d).contains(&b) => true,
2021/11/20(土) 20:49:00.01ID:iQUqB4pW
bのところが式かも知れないのでxにマッチさせてこの方がベターか
  x if (a..=d).contains(&x) => true,
2021/11/20(土) 21:28:17.44ID:i03LC++E
>>69
ありがとう 

if (a..=b).contains(&c) { println!("true") } else { println!("false"); };
って感じで試して見たけど、ガードで
(a..=b).contains(&c)にすればよさげだとわかった
2021/11/21(日) 01:21:54.59ID:VYuGYhJz
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を使えるでしょうか?
2021/11/21(日) 01:26:47.07ID:mkw0m2hl
>>73
複数のトレイトは + で繋げることができるんやで
2021/11/21(日) 01:30:30.33ID:VYuGYhJz
>>74
はい
それで何のトレイトを繋げればいいですか?という質問です
2021/11/21(日) 02:02:32.44ID:VYuGYhJz
格闘しているうちにコンパイラがstableじゃダメだとかfeature指定しろとか無茶を言って来るので従ってnightlyにしたりfeature指定したりしたところようやく動いたのですが何か変ですよね
2021/11/21(日) 02:16:44.28ID:qtUeCPjG
うーん
引数に繰り返す回数渡せばいいような気がするんだけどそう簡単じゃないのね
2021/11/21(日) 02:30:42.73ID:VYuGYhJz
nightlyにせずstableのままで範囲指定nをジェネリック化できた方いましたらやり方教えて下さい
2021/11/21(日) 02:59:34.97ID:mkw0m2hl
>>75
ドキュメントをみるかぎり PartialOrd が要求されているでよ
2021/11/21(日) 03:09:03.92ID:qtUeCPjG
やりたいことってこんな感じ?
https://play.rust-lang.org/?version=stable&;mode=debug&edition=2021&gist=f97e7a0bd7775b88065da30bcf6a6a01
2021/11/21(日) 04:34:14.82ID:VYuGYhJz
>>79
入れたけどダメでした

>>80
違う
この形です
fn ntimes_print<T>(n: T, s: &str) {
 (0..n).for_each(|_| print!("{}", s));
}
2021/11/21(日) 10:25:42.94ID:szj4saah
リテラル`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));
}
2021/11/21(日) 11:02:09.61ID:VYuGYhJz
やはり..を使う限り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);
 }
}
2021/11/21(日) 12:10:20.71ID:qtUeCPjG
https://play.rust-lang.org/?version=stable&;mode=debug&edition=2021&gist=3702e2291f04ef5e40bd5310079972b1
2021/11/21(日) 15:03:50.15ID:szj4saah
>>83
where
T: num::Integer,
Range<T>: Iterator<Item=T>,
とかにすればできるよ
2021/11/21(日) 16:08:27.58ID:VYuGYhJz
>>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);
 }
}
87デフォルトの名無しさん
垢版 |
2021/11/21(日) 18:59:28.56ID:a8amZ/lG
更に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
 }
}

これだけで動作する
2021/11/21(日) 20:22:13.92ID:ekMm5ue5
timesなら精々u64::MAX回も繰り返すことなさそうだしTからu64に変換するのではだめなの?
2021/11/21(日) 20:32:22.18ID:VYuGYhJz
トレイト境界が短くなって素晴らしい原因ですがもしかして
>>83では要のops::Range<T>型がコードに明示されてないので条件のiter::Stepを要求されてしまい
>>85ではそのops::Range<T>型をトレイト境界に登場させたためiter::Stepが不要となり
>>87ではそのops::Range<T>型を返り値として明記したためトレイト境界にも不要となった??
2021/11/22(月) 10:01:45.21ID:YMaXH3oe
指定するトレイト境界が減った代わりにAPIが劣化してる
2021/11/22(月) 11:46:24.84ID:EEj8G+es
>>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)
 }
}
2021/11/22(月) 12:13:59.22ID:qBbb57Hy
わざわざ外部crateと独自trait使って
n.times().for_each(f)にするくらいなら
(0..n).for_each(f)で十分
2021/11/22(月) 12:33:40.60ID:EEj8G+es
>>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)) }
}
2021/11/22(月) 12:45:37.77ID:EEj8G+es
>>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")
ちゃんと数値型以外でも動きました
2021/11/22(月) 15:04:22.59ID:UzgCqcLK
>>93
>それでは最初の条件のジェネリックを満たせていない
ジェネリックにしたければ(T::zero()..n).for_each(f)と書けばいいだけでしょ
単にRangeを返すだけのメソッドを手間かけて微妙に抽象化しても周りが迷惑するだけだぞ
2021/11/22(月) 15:23:28.68ID:EEj8G+es
>>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を満たしていないと言われる
2021/11/22(月) 19:21:50.63ID:fRCpO7Rh
どうしてもstableでやりたいという話ならRangeとStepを独自に用意するしかなさそう
n..mという表記は使えないが、n.times()なら支障なく実装できるかと

自分なら以下みたいに書くけどね
(T: From<i32> + PartialOrd が前提)

(0..).map(T::from).take_while(move ¦x¦ x<n)
98デフォルトの名無しさん
垢版 |
2021/11/22(月) 19:36:28.33ID:3Rtka3dv
なるほどね
2021/11/22(月) 19:55:09.41ID:5egSOJea
>>97
それだとT: From<i32>という無関係で不要な強い条件を満たさないといけないため
例えば>>93の型Xでは動かないね
シンプルにZero、One、Add、PartialOrdだけで実装したほうが良さそう
2021/11/22(月) 20:04:43.45ID:VyYbXHRo
C++じゃないんだから不毛な型パズルはやめろ
2021/11/22(月) 20:25:52.53ID:gBMgBg1g
C++よりマシ
2021/11/22(月) 20:56:45.47ID:MJpN6tlo
意味のない例で延々とよくやるわ
2021/11/22(月) 21:02:15.85ID:FLi/1Joa
カスタムな型ならRangeよりイテレータ対応が先
2021/11/22(月) 21:09:17.05ID:fRCpO7Rh
StepがunstableなのはさておきFromLiteralみたいなトレイトがあるとZeroやOneの出番が減ってうれしいのかね
T: FromLiteralの時に整数リテラルがT型の値として解釈されるようになるようなイメージ
2021/11/22(月) 21:58:50.69ID:FLi/1Joa
try_into()でできるよ
2021/11/22(月) 22:09:34.28ID:fRCpO7Rh
言葉足らずでしたね

struct Foo;
が FromLiteral を実装しているときに
let n: T = 123;
というコードを書くとコンパイラが
let n = T::from_literal("123");
といったコードに変換してくれるイメージ

from_literalはconst fnにできてコンパイル時にエラー検出できるとベター
■ このスレッドは過去ログ倉庫に格納されています
5ちゃんねるの広告が気に入らない場合は、こちらをクリックしてください。

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