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
すげーな。してはいけないレビューの典型例じゃねーか。
すげーな
282:デフォルトの名無しさん
21/12/11 20:24:40.06 Z1L5tslT.net
いやネタでしょ
283:デフォルトの名無しさん
21/12/11 20:29:51.18 /anFx7me.net
>>275
これだとarg1がSomeの時もto_string()が呼び出されて無駄なヒープアロケーションが走るのでunwrap_or_elseにすべき
284:デフォルトの名無しさん
21/12/11 22:35:07.46 oic9EtmK.net
みなさんありがとうございます
Stringにする方法は無事にこれで動きました
let name: String = arg1.unwrap_or_else(|| DEFAULT_NAME.to_string());
元の質問>>272のように&strにする方法は無いのでしょうか?
もし可能ならばto_string()のヒープアロケーションを減らせるかなという質問です
285:デフォルトの名無しさん
21/12/11 22:59:59.99 yVS9OnV5.net
>>279
Cowかな
let name: Cow<str> = arg1.map_or(Cow::Borrowed(DEFAULT_NAME), |s| Cow::Owned(s));
286:デフォルトの名無しさん
21/12/11 23:20:49.43 yVS9OnV5.net
場合によってはこれでもいいのかな?
let name: &str = &arg1.as_ref().map_or(DEFAULT_NAME, |s| s.as_str());
287:デフォルトの名無しさん
21/12/11 23:54:40.62 tYxQqCnY.net
>>281
それでもよいけど正解はシンプルなこれ
let name: &str = if let Some(ref arg1) = arg1 { arg1 } else { DEFAULT_NAME };
まずarg1を消費しないようにrefで受ける
2代目のarg1は&Stringなので自動的に&strへderefされる
288:デフォルトの名無しさん
21/12/12 02:10:02.51 h/Sb7JBW.net
1.40からas_derefっつうのがあるんだってさ
let name = arg1.as_deref().unwrap_or(DEFAULT_NAME);
でもコマンドライン引数の場合は消費しないメリットがほぼ無いのでCowのほうがいいかな
289:デフォルトの名無しさん
21/12/12 04:28:54.46 8d+idsXS.net
どの型で統一すべきかは
(1) その後に加工伸長などするならString (arg1をここで&strに統一するのは無駄)
(2) 参照するのみなら&str (DEFAULT_NAMEをここでto_stringするのは無駄)
(3) その後に判明する条件次第で両ケースありうるならCow (ただし常にCow利用はCowコストが無駄)
って感じ?
290:デフォルトの名無しさん
21/12/12 10:07:32.08 svMJrknn.net
おそらくその通りだけど、CLIツールの初回一回だけのアロケーションにそこまでこだわるのがそもそも無駄って気もする
ループ内とかでもなければ雑にString作っちゃっていいかもね
291:デフォルトの名無しさん
21/12/12 10:31:14.68 eE6Pv/WZ.net
んだ
292:デフォルトの名無しさん
21/12/12 10:46:59.45 8d+idsXS.net
>>285
今回のケースはそうだね
ただしヒープ割り当てをなるべく避ける様々な手法を把握しているか否かは色んな局面で効いてくるから
今回6通りも動くコード例が示されたことは多様に対応可能な柔軟性の良さかな
気にしなくても書けるし気にすれば効率を上げることができる点で
293:デフォルトの名無しさん
21/12/12 18:38:25.33 h/Sb7JBW.net
>>284
Cowと&strに揃える場合の一番の違いはライフタイム管理
Stringを&strにするとライフタイム管理がつきまとうから
すぐ使いきる場合以外はCowに比べてメンテナンスしにくいコードになる
294:デフォルトの名無しさん
21/12/12 18:59:43.58 3rjDzGgS.net
文字列についてはStringにするかCow<str>にするか迷うくらいならinternしちゃうのも手かと
どのライブラリが定番なのかよく知らないけど
295:デフォルトの名無しさん
21/12/12 19:49:57.76 be4Z/veb.net
>>281
> let name: &str = &arg1.as_ref().map_or(DEFAULT_NAME, |s| s.as_str());
それarg1の前の&は不要でこれで動く
let name: &str = arg1.as_ref().map_or(DEFAULT_NAME, |s| s.as_str());
さらにas_str()使うより短く書けて&**sで&strになる
let name: &str = arg1.as_ref().map_or(DEFAULT_NAME, |s| &**s);
さらに&Stringのsのままでもderefされるため大丈夫
let name: &str = arg1.as_ref().map_or(DEFAULT_NAME, |s| s);
クロージャが何もしてないからといって無くしてしまうとderefが効かず型不一致コンパイルエラー
× let name: &str = arg1.as_ref().unwrap_or(DEFAULT_NAME);
そこで明示的にderefしてやればよい
let name: &str = arg1.as_deref().unwrap_or(DEFAULT_NAME);
296:デフォルトの名無しさん
21/12/13 21:23:25.03 zBnuOauJ.net
ken okabeのqiitaの記事がまた炎上してるよ
mod_poppoにボコボコにされてる
297:デフォルトの名無しさん
21/12/13 22:30:34.09 i33Tname.net
あれ、Qiitaには垢バンされて投稿できないんじゃなかった?
298:デフォルトの名無しさん
21/12/13 23:00:30.90 Yx06Lw1d.net
ググってもよくわからないのですが、どういった方なんですか?
299:デフォルトの名無しさん
21/12/13 23:30:17.26 IeJGNs4K.net
盛大な時間の無駄になるだけなので調べてはいけない
300:デフォルトの名無しさん
21/12/13 23:59:51.20 mqpFvLOG.net
>>291
poppoとかいうやつも多様な定義や多様な解釈が存在している中で不要なイチャモンばかりだな
さらに冒頭のこれも
> JavaScriptで演算子オーバーロードを実現しようとするのは筋が悪
301:い たまたま例としてJavaScriptを用いているだけなのにそれすら理解できていない okabeは使用言語と無関係に成り立つ話をしてるだろ > reduceは二項演算ではなく三項演算として捉えるべき これも些細なことであって例えばRustなら fold()は『入力列・初期値・演算関数』の三項演算だけど reduce()は『入力列・(初期値は入力列の先頭なので無指定)・演算関数』の二項演算 とはいえokabeの方もイテレータすら扱っていないからイマイチ
302:デフォルトの名無しさん
21/12/14 00:07:08.47 LYbWtya0.net
Rust関係ねーだろ
二度とその名前を口に出すな
303:デフォルトの名無しさん
21/12/14 10:41:11.49 QBQJlKEt.net
P2P方式の2D対戦ゲームを作りたいと考えています。
おすすめのゲームエンジンやライブラリはございますか?
304:デフォルトの名無しさん
21/12/14 11:46:02.04 mpAOsF0a.net
>>297
あくまでゲームを作ることが目的なんだったら普通にUnityとかでいいんじゃない?
どうしてもRust使いたいならサーバー側で使えばいい
305:デフォルトの名無しさん
21/12/14 12:45:44.37 QBQJlKEt.net
>>298
個人的にRustが好きなので技術向上のためにもRustで作りたいと考えています。
現在はAmethystとlibp2pを用いて開発しようと考えているのですが、如何せん知識が浅くこれで目的のものが作れるのか分かりません。
是非先人の知恵をお貸しください。
Rustでの開発にロミオとジュリエットの恋ほどの壁があるという場合は、大人しくC++かUnityで作成します・・・
306:デフォルトの名無しさん
21/12/14 13:10:49.68 +JRF3Q+g.net
そんだけの情報ではなんともいえん。
見通しが立たないものを試行錯誤で作る場合には
モジュールではなくレイヤで分割したほうがいいという考え方がある。
要するに機能不足でもバグだらけでもコードが整理されてなくてもいいからとにかく「動くもの」を作って
その上に足りないものをどんどん足していくという方法論だ。
よくわかってないなら小さいもので色々やってみて知識を積み重ねるべきで、
よくわからんまま目的に向かって邁進してもあんまり技術向上にはならんよ。
307:デフォルトの名無しさん
21/12/14 13:22:32.10 QBQJlKEt.net
>>300
ありがとうございます
色々試してみます
308:デフォルトの名無しさん
21/12/14 13:53:41.90 ZTFSAiNI.net
>>299
Amethystは開発中止になったから今からやるのは微妙かも
gamedev.rsに今アクティブなエンジンやゲームがスクショ付きで載ってるから
そこからイメージにあうものを探すといいかもしれない
309:デフォルトの名無しさん
21/12/14 15:30:19.02 QBQJlKEt.net
>>302
そうだったんですね・・・
時間は掛かるかもしれませんが、色んなものを試して自分に合う物を探すことにします
310:デフォルトの名無しさん
21/12/15 07:48:40.06 inpCEPk8.net
技術を高める目的なら windows-rs や wgpu-rs みたいな一段下から積み上げるのも楽しいよ。
ゲーム作る道のりは遠くなるけど、画面に三角形出したり、キーの入力受け付けたりするだけで達成感が出てくる。
311:デフォルトの名無しさん
21/12/17 12:43:03.76 jilrKB7M.net
URLリンク(forest-watch-impress-co-jp.cdn.ampproject.org)
エディタ自体はvscodeに勝つの難しそうだがGPUIとやらが定番GUIフレームワークにならないか期待
312:デフォルトの名無しさん
21/12/17 16:39:33.05 6JlgT7vi.net
既出だったらすいません
前から疑問だったのですが、出力におけるprint!マクロのような入力用マクロが標準ライブラリに用意されていない理由ってなんですか?
313:デフォルトの名無しさん
21/12/17 18:11:01.90 tWB5K5S1.net
>>297
1対多で、broadcast するチャットルームのバックエンドなら、
Ruby on Rails 6 のAction Cable(WebSocket)が基本
JavaScript は、React, Phaser とか
【Rails】(送信時のリロード無し!)Action CableでSlack風チャットアプリを作成、2019/12
URLリンク(www.youtube.com)
314:デフォルトの名無しさん
21/12/17 18:36:09.20 ePonqmC1.net
>>306
この辺読んで
URLリンク(github.com)
URLリンク(github.com)
315:デフォルトの名無しさん
21/12/17 23:52:56.58 6JlgT7vi.net
>>308
熟読させていただきました。
私としてはたった数十行のコードであること且つ他のほとんどの言語に存在しているものなのであっても良いのかなと思ったのですが、Rustの基本理念を考えると慎重になる理由も理解出来ました。
Rustの経験が浅い私目線ではあまり腑に落ちませんでしたが、皆さんはどう考えていますか?
316:デフォルトの名無しさん
21/12/18 15:05:52.37 JzdvFl4u.net
熟読はしてないけど、外部ライブラリで簡単に実現可能であるなら
直感的には、本体メンテナの苦労を増やすほど価値があると思えないし別にいらんかな
こういうIOとかCLIプロンプトらへんの機能ってどうしても好みが分かれるというか、
ユースケースによって必要な機能がかなり違ってきちゃうと思うし
317:デフォルトの名無しさん
21/12/18 20:46:47.80 w6oxugk6.net
C++のiostreamが失敗作だから慎重になるのもわかる笑
318:デフォルトの名無しさん
21/12/18 23:17:58.09 SfwydFh9.net
>>306
C言語でのprintf相当はあるのにscanf相当がstdにないのはなぜか?ですね
逆になぜprintf相当がRustの標準ライブラリにあるのか?を考えてみると
(1) 読み書きの非対称性
人間が読むためにプログラムが書くことはエラー表示を含めて利用必須かつ頻出
一方で人間が書いたものをプログラムが読むことはレア
ファイルや通信相手への読み書きは各プロトコル/各API/シリアライズ等で対象外
(2) コンパイラサポート
print!やformat!等は頻出するので効率面からコンパイラサポートが効率的
実際にそれらが利用しているformat_args!はコンパイラ内蔵マクロ
(3) 標準ライブラリ採用
外部ライブラリで実現可能なものは採用しないが基本
今回はコンパイラ内蔵マクロだから採用
つまり出力は頻度と効率化で採用がクリアされたけど
入力は外部ライブラリで十分かなと