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/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にできてコンパイル時にエラー検出できるとベター
2021/11/22(月) 22:10:11.00ID:fRCpO7Rh
>>106
TはFooの間違い
2021/11/23(火) 07:57:08.78ID:9DtS3af5
ねこ
2021/11/23(火) 10:28:33.41ID:8Ju98kPx
https://github.com/rust-lang/team/pull/671
Coreチームがクソだからやめたった、ってこと?
2021/11/23(火) 13:56:03.32ID:b1gEfTjX
const fnが言いたいだけやろ、だれが=演算子でcopyでもなく、言語上ないことになってるコンストラクタでもなく
そんな特異なトレイトを勝手に呼ぶのが嬉しいねん、なにがバター犬や
2021/11/23(火) 14:58:36.02ID:s6k3uLQ1
>>110
= が特殊な振る舞いをするのではなくて
整数リテラルが組み込み整数型についてgenericな数値になるという振る舞いを
FromLiteralを実装した型に広げようという案のつもりです
2021/11/23(火) 20:05:14.41ID:1c3aeddQ
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型でも動いたよ
2021/11/23(火) 20:38:59.43ID:rocYZd+S
特徴もないリテラルを勝手に解釈するとかあり得んわ
現状だって0b0011u32とか0x80u32とか書いてるのに、型定義が横にあるからそれでコンパイル時に
パース処理したいなんてそんな都合の良い言語ちゃうだろ、Raw stringのr"123"とか、Raw bytesとか
とも違うし、確かにfrom_strが"123"を解釈するけどやるにしても、let x: i32 = "123";が通ってから。
でもコンパイル時とはいえ自動型変換に見えるコードは承認しないと思うし、直行性も下がる
こんな場末の酒場みたいな所で言ってもコミッターどころかforと高階関数で揉める駄スレにどうこう出来る内容ちゃう
2021/11/23(火) 20:58:16.83ID:Oek7vlRG
>>112
数値だからAdd+Oneが成り立つわけで
型Xとか何の意味もないぞ
2021/11/23(火) 22:14:31.32ID:qrGqDm2c
>>114
でも現実のプログラミングでは数値を生で使うよりも
struct Counter { usize } とか
struct Age { usize } とか
struct XxxLength { usize } とかにして
型が異なることをはっきりさせて安全に使いますよね
そして付加情報があればstructのフィールドが複数になることもあったり
あるいはstruct std::time::Durationのようにnanoからsecまで扱えるようにしてもAddをimplして使いますよね
つまり生の数値だけを対象にしていては視点が狭いと思うのです
2021/11/23(火) 22:15:57.50ID:qrGqDm2c
>>115の{ }は( )の誤記ですw
2021/11/24(水) 03:12:15.58ID:P1gN11rG
Stepがstable入りしたら要らなくなる話になにとんでもない破壊的変更を持ち出しているんだ
2021/11/24(水) 03:25:07.43ID:gceGN8+W
Age型をn.times().for_each()
Length型をn.times().for_each()
Duration型をn.times().for_each()

ジェネリックw
2021/11/24(水) 05:02:23.28ID:e1u6MioL
>>118
timesより>>112のm..n rangeイテレータが汎用的でいいんじゃね?
2021/11/24(水) 15:22:53.32ID:zkfKZqQ7
>>119
times()ですら無駄なのに>>112とかありえないよ
無価値どころか害悪レベルなんだけど
2021/11/24(水) 17:05:44.23ID:5wn/1hS5
>>117
コンパイラや標準ライブラリの変更で型推論の結果が変わることは破壊的変更扱いされなかったっけ
2021/11/24(水) 17:26:56.67ID:Zq3lnaBh
>>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する感じですかね
2021/11/24(水) 17:39:27.77ID:Zq3lnaBh
>>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を書かなくても済むように出来そう
2021/11/24(水) 17:47:29.36ID:Zq3lnaBh
>>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)]
そして当然ながら動きました
2021/11/24(水) 17:56:43.20ID:Zq3lnaBh
>>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型についても動きました
2021/11/24(水) 18:18:05.49ID:AgL0hXz4
誰も見てねーから別のところでやれ
2021/11/24(水) 19:13:18.31ID:5wn/1hS5
話題がないんだもん
なんか話題を提供してくれ
2021/11/24(水) 23:19:00.01ID:e1u6MioL
結局Derefみたいにコンパイラが自動的に適用して変換してくれればそのintoやfromをプログラムには書かなくて済むんやろ
2021/11/25(木) 23:08:27.91ID:QVGqalzl
ファイルを開く操作って普通に考えたらFile::open_ほにゃらら()みたいなメソッドにOpenOptionsを渡すほうが自然だと思うんですが
OpenOptions::open()みたいな方法を取ってるのってどういう理由からなんでしょうか?
2021/11/25(木) 23:59:01.72ID:88pS2ZzI
>>129
・その方がメソッドチェーンで見やすい
・複雑な構造体を用意してそこに様々な値をセットして指定するインターフェースは非常に大変
・そのうえOS毎に違う部分もある
2021/11/26(金) 00:22:31.18ID:aH1+xhzE
>>129
Rustは関数定義でデフォルト値のあるオプション引数をサポートしてないから
オプション引数的な使い方をしたい場合はビルダーパターンを使うのが一般的

File::options().read(true).create(true).open("foo.txt”);みたいな使い方になる
2021/11/26(金) 09:37:38.31ID:kuMbCEJE
うーん
ダサいな
もっときれいにならんの?
2021/11/26(金) 09:50:01.95ID:kuMbCEJE
メソッドチェーンって過去の遺物だよね?
バグの温床だし
長いとどこかで消し忘れや二重指定が出て本人が気が付かなくなる
長くなっただけで破綻するんだからおかしい
2021/11/26(金) 10:00:30.74ID:5+U4u14D
>>133
一般的に今どきのプログラミング言語は全てメソッドチェーンが主流
もしかしてメソッドチェーンを使わない古い言語使いの方ですか?
■ このスレッドは過去ログ倉庫に格納されています
5ちゃんねるの広告が気に入らない場合は、こちらをクリックしてください。

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