乙 シャドーイングが困るのって関数がよっぽど長いときだけなんで問題にならんよね
>前スレの質問「間違って同じ変数名つけたコード片突っ込んでしまった系だと他言語にあるらしい2重宣言エラーないの怖くないですか?」 他の言語でもスコープが違えば二重宣言エラーになりません つまり他の言語でも意図しない同名変数ミスは同じように起き得ます 例えば以下はC言語の例 int c = 100; int main() { int c = 200; { int c = 300; } printf("%d\n", c); /* コンパイルは通って 200 となる */ } Rustのシャドーイングは同じブロックスコープでも同名変数が通りますが 仮にミスで同名変数を付けたコードを挿入してしまったとしても ・その時点で先行の変数が既にライフタイム尽きていれば影響ゼロ ・その時点で先行の変数のライフタイムがあって割り込む形になる場合 ・それ以降で二度の消費が発生して借用エラー発生となる可能性が高い ・先行の変数が一度も使われていない段階なら未使用warning発生 ・型が違っていれば割り込む形であっても型違いでエラー発生 と影響ゼロもしくは引っかかる可能性が高いでしょう >>4 おっしゃる通り、そもそも意図しない同名変数に気付かない時点で「その関数は長すぎ」ですね ある値をちょっとだけ処理したものにどんな名前を付けたいかってのは、 まあ適した名前があるならそれに越したことは無いけど 同じ名前を付けることによってたいした違いはない、かつ以前の値にアクセスすることはもうないことが明示できるので それはそれで使いようはあるよ。 以前の値にアクセスしないという意思を持ってシャドーイングを使うのでそれで困ることはない。 うっかりで同じ名前を付けてしまった場合も大抵の場合は型の違いとかで検出されるし。
何を問題にしてるのか分からんけど、コンパイラから見たら、同じ変数名宣言でも連番で構文解析しているわけで ブロックスコープによりシャドーされても何ら関係ないが、インライン展開して最適化するrustコードだと問題が 出る場合もありうる。それとシャドーと以前の値にアクセスすることはもうない事は意味が違う
>>7 間接的にアクセスしうるとかいうのはもちろんあるけど、 あくまでもプログラマが読み書きする上での意図として明示するという意味ね。 実際にもう (直接には) 使えないんだから使えないという意味だよ。 ん?まあありうるだろうけど、そんな意図を持ってシャドーイングをするコードは捨てろよ?明示じゃねーわ
>>996 C/C++だとスコープ中で外のスコープの宣言と被ってたら警告出るよね >>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したけどその警告出ない intellijでも、シャドーイングがあったときは警告出てなかったっけ?
そんなことよりもだ!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);
let acc1: u32 = (0..).map(|n| n * n) .take_while(|&n_squared| n_squared < upper) .filter(is_odd) .sum()
>>5 不意のミスでも結構コンパイルで引っかかってくれるようで安心しました。Rustさんよく考えられてますね。知りたかったことが書いてありました。ご丁寧にありがとうございます shadowing嫌いな人はdeny(clippy::shadow_same)などすればよい
>>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に 比べ意図が明確になりますが、それは自己満足の幅が大きいとも言えます >>18 のコードは大きな配列とかメモリ大量消費とかしないだろ 小さなstructを以下の4つ消費するのみで誤差 RangeFrom, Map, TakeWhile, Filter >>22 18のコードは配列ではなくイテレーターなのでそうですが、map,take_while,filterのほうが 好みの人が多いとは思いますが、私が言いたいのは何でもかんでもHigh orders functionに して書くのはよく知らないと、リスクがあるということとブレークポイントなどを仕掛けて 経過は見れないということです。日本語が読めない人はもう少し考えましょう メソッドチェーンなら、メソッドごとに単体テストができるし、テストを用意しとけばすぐにバグがわかるような気がするが。
ほんと日本語読めない奴ばっかり。経過いうてるのにメソッド毎だとか、バグのことなんて言って ないのに(特殊な例を言えば配列の場合でmap().map()などとしたメモリー使用量)を言っている のに、そもそも個人的には言うてんのに、そんなに説得したいのか?
翻訳は、Chrome, Edge の翻訳機能、 Google の翻訳サイトとか、DeepL とか
言語は素晴らしいのにやってる奴が意識高い系のウンコ野郎ばっかり
まあコミュニティに馴染めるかどうかも本人の適性によるところがあるしな
>>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 のメソッドチェーンを使うかの話に配列は一切関係ない 配列とスライスとベクタが別物なのこういう場所で会話するときに地味に混乱しがち
Stringとstrって日本語でどう呼び分けるんですか?
>>30 こういう内容ゼロの事を言ってる意識高い系が普及を邪魔してる。自身が”何度も使えば配列地獄になるけど”と 認めてるにも関わらず、最初からイテレータを使えばと先に言ってることをそのまま返す 何が”したがって”なのか全然分からんが、説明した気になり、”チェーンを使うかの話に配列は関係ない”と まとめる。そもそもチェーンを批判なんてしてない(対象によりけり高階関数の連続使用はメモリーを圧迫する 可能性があるということだけ)それとも言い負かしたいだけの気持ち悪さをそんなに使用者に伝えたいのか? >>25 君が二つの全く異なるmap()をゴチャ混ぜにして皆を騙して混乱させているだけだ 我々はfor文で回すかイテレータをチェーンするかの話をしている 君が持ち出した[T; N]::map()は配列を配列に変換するのみでイテレータと一切関係ない 我々はmap()に関しては一貫してIterator::map()の話しかしていない ほんとここに来るやつは皆を騙す詐欺師の気持ちや悪い奴ばかり、一切関係ないぞ!
どんなに適当に書いてもコンパイラさんがうまいこと最適化してくれるのでヨシ!
最初から大して違いも出ないが、分かりやすさを重視するなら高階関数かな? これはこう書け!と口煩くいってくる奴が理解できないけど… でも裾野を広げるためには未熟で冗長なコードでも許容しなきゃね、1つもfor無しで デカいプロジェクトを書くことなんて無いし、趣味でやってるだけなら縛りもありだが
>>39 rayonなどに移行しやすいという意味? rustの得意とする高パフォーマンス領域だとactix(-web)なんかはスレッド数=物理コア数で 下手に並列化しないほうがrequest/secは高いから、標準の高階関数も並列にはなっていない 訳で、バッチ処理みたいなその時に他の処理が走ってなくて、如何に処理時間を短くするか というプログラムなら最初からRayonとか最初に導入してそう… 状態共有しない並列起動だけなら、thread::spawn(|| { for i in 0..x {...} }でも比較的に 簡単に出来るが、そういう意味では無いんだろう
>皆を騙す詐欺師の気持ち悪い奴ばかり 都合悪くなると話題変えて逃げるしな
async/awaitが高階関数と(今の所)相性良くない
>>44 今のところとしてるのは改善策が取り込まれる見込みだから? それとも単に将来のことはわからない程度の意味? 静的チェックでオールOKみたいな言語って意識高い系のバカを呼び寄せやすいんだよ。 奴らは動的テストなんてしたくないって意識をすぐ誤魔化す癖がついてる。
自分のことを言われて怒っちゃったのか。 わかりやすいね。
>>32 ストリングとストラ もしくは ストリングとストリングスライス うん、完全理解した。こんスレは同じrustガンダム乗りなのに連邦の白い悪魔以外は認めないんだ どっちでもいいじゃんか・・・ Vecに入ったi32(空っぽの可能性もあり)の平均を計算するのってどうするのが一番スマート?
副作用持つときはfor文でそれ以外はメソッドチェーン繋げてやります
>>34 イテレータの高階関数の連続使用でメモリーを圧迫することはないと思います 各イテレータ自体はコンパクトな構造体ですし高階関数自体の処理も基本的にはメモリーを圧迫するものではありません ただし全ての要素を受け取らないと算出できない物もあるため一時的に長いVecを使うことになるイテレータもあります この場合はイテレータを用いずに自分でforなどを回す方法をとっても同様にして一時的に長いVecを使うことになるでしょう そういや平均求めるメソッドってstd::iterにもitertoolsにもないんか?
>>47 Rustには動的テストを容易にする仕組みが入ってるしRustの話ではなさそうだ そんな簡単な実装を一々stdに入れてたら、糞関数だらけのLLライクになるじゃん?
>>58 std::iterに入れろとまでは思わないけど、 sumもcountもイテレータ消費しちゃうから微妙にめんどくさくない? >>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()); } オーバーフローしないように型を選択することとか、 汎用的に使えて問題を避けることを考えたら平均を計算するのってそんなに簡単な話ではないと思う。
Vecとかに入った複数のtokio::task::JoinHandleをjoinするのはどうやるんでしょうか?
thread::JoinHandleは並列で(マルチスレッドとなり) join()で待つ task::JoinHandleは非同期で(シングルスレッド時は並行、マルチスレッド時は並列となり) awaitで待つ
futures::future::join_allでいいんじゃね
task::JoinHandleの方はFuture trait実装なので 列挙ならfutures::join!(f1,f2,f3,...)マクロ VecならIntoIteratorに対応のjoin_all(v) >>65 let mut a = 'a'; let mut d = 'd'; match 'b' { a..=d => { println!("true") } _ => { println!("false") } } matchでcharの範囲指定ができるからって試したんだけど、 変数に入れたcharだと範囲指定ってできないの? これだとあんまり意味ないような
パターンには代入される側の変数しか書けないけどガードは自由自在 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); }
そして範囲指定を使いたいんだったらこう _ if (a..=d).contains(&b) => true,
bのところが式かも知れないのでxにマッチさせてこの方がベターか x if (a..=d).contains(&x) => true,
>>69 ありがとう if (a..=b).contains(&c) { println!("true") } else { println!("false"); }; って感じで試して見たけど、ガードで (a..=b).contains(&c)にすればよさげだとわかった 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を使えるでしょうか?
>>73 複数のトレイトは + で繋げることができるんやで >>74 はい それで何のトレイトを繋げればいいですか?という質問です 格闘しているうちにコンパイラがstableじゃダメだとかfeature指定しろとか無茶を言って来るので従ってnightlyにしたりfeature指定したりしたところようやく動いたのですが何か変ですよね
うーん 引数に繰り返す回数渡せばいいような気がするんだけどそう簡単じゃないのね
nightlyにせずstableのままで範囲指定nをジェネリック化できた方いましたらやり方教えて下さい
>>75 ドキュメントをみるかぎり PartialOrd が要求されているでよ >>79 入れたけどダメでした >>80 違う この形です fn ntimes_print<T>(n: T, s: &str) { (0..n).for_each(|_| print!("{}", s)); } リテラル`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)); }
やはり..を使う限り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); } }
>>83 where T: num::Integer, Range<T>: Iterator<Item=T>, とかにすればできるよ >>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); } } 更に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 } } これだけで動作する
timesなら精々u64::MAX回も繰り返すことなさそうだしTからu64に変換するのではだめなの?
トレイト境界が短くなって素晴らしい原因ですがもしかして >>83 では要のops::Range<T>型がコードに明示されてないので条件のiter::Stepを要求されてしまい >>85 ではそのops::Range<T>型をトレイト境界に登場させたためiter::Stepが不要となり >>87 ではそのops::Range<T>型を返り値として明記したためトレイト境界にも不要となった?? 指定するトレイト境界が減った代わりにAPIが劣化してる
>>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) } } わざわざ外部crateと独自trait使って n.times().for_each(f)にするくらいなら (0..n).for_each(f)で十分
>>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)) } } >>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") ちゃんと数値型以外でも動きました >>93 >それでは最初の条件のジェネリックを満たせていない ジェネリックにしたければ(T::zero()..n).for_each(f)と書けばいいだけでしょ 単にRangeを返すだけのメソッドを手間かけて微妙に抽象化しても周りが迷惑するだけだぞ >>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を満たしていないと言われる どうしてもstableでやりたいという話ならRangeとStepを独自に用意するしかなさそう n..mという表記は使えないが、n.times()なら支障なく実装できるかと 自分なら以下みたいに書くけどね (T: From<i32> + PartialOrd が前提) (0..).map(T::from).take_while(move ¦x¦ x<n)
>>97 それだとT: From<i32>という無関係で不要な強い条件を満たさないといけないため 例えば>>93 の型Xでは動かないね シンプルにZero、One、Add、PartialOrdだけで実装したほうが良さそう StepがunstableなのはさておきFromLiteralみたいなトレイトがあるとZeroやOneの出番が減ってうれしいのかね T: FromLiteralの時に整数リテラルがT型の値として解釈されるようになるようなイメージ
言葉足らずでしたね struct Foo; が FromLiteral を実装しているときに let n: T = 123; というコードを書くとコンパイラが let n = T::from_literal("123"); といったコードに変換してくれるイメージ from_literalはconst fnにできてコンパイル時にエラー検出できるとベター
const fnが言いたいだけやろ、だれが=演算子でcopyでもなく、言語上ないことになってるコンストラクタでもなく そんな特異なトレイトを勝手に呼ぶのが嬉しいねん、なにがバター犬や
>>110 = が特殊な振る舞いをするのではなくて 整数リテラルが組み込み整数型についてgenericな数値になるという振る舞いを FromLiteralを実装した型に広げようという案のつもりです 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型でも動いたよ 特徴もないリテラルを勝手に解釈するとかあり得んわ 現状だって0b0011u32とか0x80u32とか書いてるのに、型定義が横にあるからそれでコンパイル時に パース処理したいなんてそんな都合の良い言語ちゃうだろ、Raw stringのr"123"とか、Raw bytesとか とも違うし、確かにfrom_strが"123"を解釈するけどやるにしても、let x: i32 = "123";が通ってから。 でもコンパイル時とはいえ自動型変換に見えるコードは承認しないと思うし、直行性も下がる こんな場末の酒場みたいな所で言ってもコミッターどころかforと高階関数で揉める駄スレにどうこう出来る内容ちゃう
>>112 数値だからAdd+Oneが成り立つわけで 型Xとか何の意味もないぞ >>114 でも現実のプログラミングでは数値を生で使うよりも struct Counter { usize } とか struct Age { usize } とか struct XxxLength { usize } とかにして 型が異なることをはっきりさせて安全に使いますよね そして付加情報があればstructのフィールドが複数になることもあったり あるいはstruct std::time::Durationのようにnanoからsecまで扱えるようにしてもAddをimplして使いますよね つまり生の数値だけを対象にしていては視点が狭いと思うのです Stepがstable入りしたら要らなくなる話になにとんでもない破壊的変更を持ち出しているんだ
Age型をn.times().for_each() Length型をn.times().for_each() Duration型をn.times().for_each() ジェネリックw
>>119 times()ですら無駄なのに>>112 とかありえないよ 無価値どころか害悪レベルなんだけど >>117 コンパイラや標準ライブラリの変更で型推論の結果が変わることは破壊的変更扱いされなかったっけ >>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する感じですかね >>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を書かなくても済むように出来そう >>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)] そして当然ながら動きました >>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型についても動きました 結局Derefみたいにコンパイラが自動的に適用して変換してくれればそのintoやfromをプログラムには書かなくて済むんやろ
ファイルを開く操作って普通に考えたらFile::open_ほにゃらら()みたいなメソッドにOpenOptionsを渡すほうが自然だと思うんですが OpenOptions::open()みたいな方法を取ってるのってどういう理由からなんでしょうか?
>>129 ・その方がメソッドチェーンで見やすい ・複雑な構造体を用意してそこに様々な値をセットして指定するインターフェースは非常に大変 ・そのうえOS毎に違う部分もある >>129 Rustは関数定義でデフォルト値のあるオプション引数をサポートしてないから オプション引数的な使い方をしたい場合はビルダーパターンを使うのが一般的 File::options().read(true).create(true).open("foo.txt”);みたいな使い方になる メソッドチェーンって過去の遺物だよね? バグの温床だし 長いとどこかで消し忘れや二重指定が出て本人が気が付かなくなる 長くなっただけで破綻するんだからおかしい
>>133 一般的に今どきのプログラミング言語は全てメソッドチェーンが主流 もしかしてメソッドチェーンを使わない古い言語使いの方ですか? パイプライン演算子大好きとかLisp方面から来た人とかなのかも……
え?メソッドチェーンってjqueryが流行ってたころの名残でしょ? 10年ぐらい前 メソッドチェーンなんて書いててだるいだけ
>>136 JavaScriptも今は関数型プログラミングが主流へと変わりメソッドチェーンだし外部ライブラリを使うインタフェースもメソッドチェーンがよく使われる そしてRustも同様 どこの古い世界から来たお客さんですか? >>133 ビルダーは二重指定しても問題ない 消し忘れはどういう指定方法でも発生するからテストで防ぐ以外ないよ テスト書かない文化の人? >>135 パイプ演算子も同じ問題抱えてるし Lispでも今はthreadマクロで処理順に書く >>139 二重指定は一方でtrue一方でfalse指定しているパターン >>132 どうやったら綺麗なのか参考のため教えて Rustじゃなくて他の言語でも 仮想の言語でもいいよ 通常のCのオープン関数のほうが100倍キレイで簡潔で合理的だと思うが個人差はあるんだろうな File::options().read(true).create(true).open("foo.txt”); これを書くときにリードがtrueだな、createもtrueだな、そしてファイル名はfoo.txtだと言う思考順序で コード書くとは思えないんですよ常識的に
これが美しいと言う人はそれこそ逆ポーランド次元から来た異次元人だと思うよ
おまいら日本語否定ですか。 >>144 は設計が悪いんじゃない? File::options().readwrite().newfile().open("foo.txt”); なら自然だろ。 >>144 ファイルを開こうと思うときにoptions型から思考がスタートする人は天才なんだろうな それか飼いならされた人
言語否定じゃなくてライブラリ設計がおかしい 実用無視の人が設計するとこうなる
File::open_with_options("foo.txt", &OpenOptions::new().read(true).create(true)); どっちが良いとか悪いとかじゃないと思うがなあ
OpenOptionsって名前で明らかに「ファイル開く時のオプションですよ」って名前なのにファイル開く操作まで持ってるから気持ち悪いんだよな 同じモジュールでディレクトリはDirBuilderとかあるんだから普通にFileBuilderとかにすりゃええやんとか思っちゃうけどなんか理由があったんかね
確かに他だったらoptionのインスタンスを作ってopenに食わせる感じだな それもどうかと思うけど ほぼ定数みたいなものをわざわざ作って食わせるなんて
慣れない人にとっては分かりにくいAPIにならざるを得ないのは確か ただ他の言語でオプション引数やオーバーロードが2桁あるのが当たり前になってるようなライブラリを見るとそれぞれ一長一短あるなとは思う
>>154 associated constくらい用意してもらってもいいかもね >>153 OpenOptionsはrust1.0以前からあるものなのでその頃はビルダーのイディオムがなかったのだと思う DirBuilderはrust1.6で追加されたものなので比較的新しい > 通常のCのオープン関数のほうが100倍キレイで簡潔で合理的 さっぱりわからんけどw 別に File::options().readwrite().newfile().open("foo.txt”); がいいとも悪いとも思わんし cのopenがいいとも悪いとも思わん どんな素晴らしいopen見せてくれるのかと思ったらガッカリした
OpenOptionsが分かりにくいってのはそのとおりだと思うし、実際みんなそう思ってるみたいで ちょうどFile::optionsにするRFCが通るところだよ
>>159 stabilizeのPRが10日前に取り込まれたから1月にはリリースされそう >>158 FILE * fopen(const char * filename, const char * mode); こっちの方が100倍良い >>161 それはfopenの仕様(引数の意味とか)を知っているのが前提だからなぁ。 filenameとmodeの順番を間違えたら読み間違える。 メソッドチェーンと比較するなら名前付き引数のメソッド呼び出しじゃない? あるいはインテリセンス環境下という条件付きか。 仮想のコードで例が適切かどうかわからないけど File::options().readwrite().newfile().read().open("foo.txt”) と書いて直後で書き込めねえええええよと叫ぶよりfopenの方がいいだろ;
あ、ミスった それはさておき例のメソッドチェーン見ても瞬時に不安しかよぎらない readwrite() が中で read(true).write(true) してるとして readonly() が中でread(true)しかしてないんじゃないかとか不安
>>162 > 名前付き引数のメソッド呼び出しじゃない? 俺も最初はそう言う話が出てくるのかとおもったら cのopenを有りがたがってる奴が出てきて呆れた >>134 メソッドチェーンと関数チェーン|パイプラインが区別できてないんでは? メソッドチェーンってレシーバの記述を省略できるとか文の数が減るとかでしかないでしょ。 >>166 fopenとゴミみたいなメソッドチェーン どっちがバグの温床だ? cのfopenはメソッドチェーンより簡潔だ 本当は他の言語のenumでいいんだけど Open("rust.txt",FileMode.Open, FileAccess.Read) これでバグは出ない
そんなに変わらへんやん、むしろuseで引き込む名前が増えて美しくないやん
foo(true, false, true, false) や foo(None, None, None, Some(true)) みたいな呼び出しよりは100倍マシ
ところでreadwrite()とかnewfile()ってなんですか?
OpenOptionsの名称問題はともかく なぜメソッドをチェーンさせているかというと理由は明白で OpenOptionsExtトレイトをuseすると使えメソッドが拡張されて.mode()などが使えるようになるからだよ
チェーンの話なら、出来るだけチェーン派が多いけど.NETのLINQみたいになったら嫌? それとも歓迎?LINQ形式のほうが分かりやすい。前の高階関数の話もそうだけど… OpenOptionsはなんでもかんでもトレイトの弊害の気もする articles = from a in articles orderby a.published descending select new article_st; https://github.com/StardustDL/Linq-in-Rust let e: Vec<i32> = linq!(from p in x.clone(), where p <= &5, orderby -p, select p * 2).collect(); このプロジェクトはcloneしてしまうところがダサいが、MSが参画しているということはありうるわけで >>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では詰む >>176 LINQはSQL風になる糖衣構文だからちょっと違うんちゃう。Rustに導入されても他の世界感と異質だし……。 個人的にはあれがSQL屋以外に直感的とは思えないw >>174 rustは第一引数がselfの場合にメソッドのレシーバーになるのだから、実質パイプライン それはElixirと同じ「なんちゃってパイプライン」でメソッドチェーンが実現できるというだけでは。 ここで欲しいと言われているのは大元のF#のように自由関数やその部分適用が使えるパイプラインのことだろう。
自由関数ってなんだっけ? 自由変数ならわかるんだけど
定義を示してほしいわな F#界隈にもRust界隈にも無い用語だし
何気なく使ってたけど調べてみたらC++用語だったか。しかも日本じゃ「フリー関数」という表記の方が一般的なんだな。 メンバー関数(メソッド)ではない素の関数ということで。
その意味ならElixirは自由関数も部分適用も使えると思うが・・・
そっちか。 Elixirのパイプラインの使用例でよく見る形式が部分適用とは違うんで使えないものと思っていた。すまん。 部分適用は別の表記になるんだな。
C++でもフリー関数と言われたら free() のこと思い浮かべそうだが
>>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(); >>189 どこが見にくいのか具体的に教えて use itertools::Itertools; let y: Vec<i32> = x .filter(|p| p <= &5) .sorted_by_key(|t| -t) .map(|t| t * 2) .collect(); >>185 > 何気なく使ってたけど調べてみたらC++用語だったか どこの世界に非メンバ関数をフリー関数という馬鹿がいるのか詳しく CかC++の規格のどこを参照すれば定義されているのか詳しく その「フリー関数」とはクラスのメンバー関数ではない非メンバー関数として クラスのない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)); } }
メソッド呼び出しできない関数は「フリー関数」ってことでええんちゃう? 非メンバ関数って呼ぶほうが断然一般的だとは思うが
>>197 selfがIteratorだからクラス志向だとprint_all()はIteratorクラスのメンバーかな しかしそのPrintAllはIteratorを継承してないから難しい >>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)); } } 重要なのはフリー関数の定義じゃなくてパイプライン演算子の適用範囲だぞ
>>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の型で示す (つづく) >>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()が解決できず失敗する 自分でこのコードを毎回間違えずに書くのは面倒なのでマクロ化されるなら採用 >>201 こういうのは手書きするのではなくて derive で良い感じに実装してほしい あとrustdocの出力がごちゃつきそうなのが気になる config部分が静的に決まってるならいいけど UIやコマンドラインから動的にconfigしたい場合Builder traitを用意してtrait objectとして持ち回して .execute()するためにdowncastする必要が……とかで二度手間になりそう
>>201 のコードがいいと感じる感性が理解できないよ >>201 難しい話をすると ビルダはディレクタに対して抽象化されており 一個のディレクタが複数のビルダをケアできることを考えると 必須パラメータというのをビルダ実装ごとに準備するなら ビルダというインタフェースがディレクタに対して実質破綻してると思う でも ビルダインタフェースが正しい呼び出し順を想定してたりするのをアリとするなら 必須パラメータも同じように勝手に想定しておいて 使う側にはドキュメントなりなんなりで勝手に指示だしとけば十分とも思う そんで 呼び出し順や必須か否かに想定を置きたくない 呼び出し側に完全な自由度を与えたビルダインタフェースを提供したいなら インタフェース Fooビルダ {Fooビルダ a(); Fooビルダ b();} クラス 実際のビルダ implements Fooビルダ { Foo(必須な何か x) {略} // コンストラクタで与えるとか Fooビルダ 必須なc() {略} /* 実際のビルダ固有のメソッドとして与えて new 実際のビルダ().必須なc().a() などと呼ぶか Fooビルダ a() {略} Fooビルダ b() {略} Foo create() {略} } のようにして、ビルダインタフェース側だけはクリーンに守っておけばスッキリかも? 元のURLにあるように何が必須かをパラメータ化したいという欲求は解消してないのは認める ここまで意味不明な文章書けるのって逆に凄いよ このレベルは久々に見たわ
Javaコードの識別子の部分を日本語で書いて GoFのオリジナルのBuilderパターンを説明しただけっぽい なんでRustスレでそんな話をしたのかだけは完全に謎だが……
普通のBuilderであまり困ったことないからなあ 操作によって遷移していく状態があって、状態ごとに可能な操作が違う(呼び出せるメソッドが違う)とかなら意味あると思う(Socketのbind->accept->read/writeみたいな) ただこういうものはもはやBuilderと呼ぶべきものではないと思う
>>217 もっと単純に let z: *mut i8 = *s; も通らないので これはDerefMutの条件であるmutable contextを満たしてないのではないか そしてもちろん let z: &mut i8 = *s; は通るし **s = 88; も通るからDerefMut自体は機能している >>218 >>219 おお、そのものズバリが あざます! 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); } } }
>>223 >> ブロック式最後の式の値は、ブロック式の値として返るので もちろんおっしゃる通りでその例でも>>221 の例でもif式の値は明瞭に()ですね >> drop されるタイミングがブロックの末尾よりも後の箇所になるということかな ブロック式の値として返るのは()ですから変数lineのdropタイミングが後にはならないように思うのですがどうなのでしょう? help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped ↑エラーメッセージに理由書いてるよ
>>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[..] {} この差をどう見るのか教えていただけますでしょうか >>224 ・lineの寿命はif-let式の寿命と一致する ・ブロックの最後の文の寿命はブロックを囲む文の寿命と一致する という仕様になっていると思われる 型は関係なくてブロック内の最後の式か否かが重要 >>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が解放されるからライフタイムのエラー そのうち修正されるかもしれないけど今はそういう動きってこと >>226 >let x = line.splitn(3, ' '); >if let [_first, _second, _rest] = x.collect::<ArrayVec<_, 3>>()[..] {} すまん、投稿ミス。 値を返したいときはセミコロンを追加するだけじゃだめだから 一旦変数で受けてからその変数をブロックの最後に書く
ふむむ 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) {}
なんでセミコロン付けると一時オブジェクトの寿命変わるんだって思ったけどそういえばC++にもそんな仕様あったな…… 完全式じゃなくて戻り値に使うから破棄されないってことか
>>230 ブロックの最後で返す値は変数に入れなくても複雑な式でも大丈夫ですよ なんでBufReadトレイトはあるのにBufWriteトレイトは無いんですか?
>>234 BufReadはReadに対して追加の機能(行単位の読み込みなど)があるけど Writeに対して追加すべき機能がないからBufWriteは存在しないのではないかと思われる flushしなけりゃ下のレイヤのどこかで勝手にバッファリングされることがほとんどだから用意する意味がないんだろな。
>>237 いやBufWriteトレイトが存在しないだけであってBufWriterはちゃんとある そしてBufWriterがバッファリングする最後の要 誰かが勝手にバッファリングしてくれることはない 例えば何行もファイルに書き込む時にBufWriter使わずに各行毎にwriteしてたら遅くなる REPL的にコードスニペットの実行確認するために使ったりはできるのかな 今はplaygroundで事足りているが
Read::read_to_end()に空のVecを渡した時に戻り値のusizeとVecのlen()が違う値になる事って有り得ますか?
同じと思う 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);
> 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)));
>>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使用がベストアンサーなのか!? >>244 flat_mapを使えば良い let r = (1..100).flat_map(¦x¦ (1..100).map(¦y¦ (x, y))).find(¦(x, y)¦ x * y > 1234); 結局見やすくこう書けるといいんだよね 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; } } } }
最近知ったボクの大発見書いていい? let a: Option<i32> = None; これは let a = None::<i32>; と書ける 関数のパラメータとして渡そうとして f(None)で怒られたとき f(None::<i32>)として怒られない しょうもないレス失礼いたしました
>>248 曖昧性を確定させる::<型>の指定は色々なところで出てくるね 例えばcollect::<Vec<_>>()とか ブロックやクロージャの返り値でOk::<(),std::io::Error>(())とか >>247 itertoolsのcartesian_productがほぼそれ >>248 型パラメータは Option のパラメータなので順当に考えれば Option::<i32>::None と書くべきだし実際にそれで通るんだけども、 歴史的経緯でバリアントにも付けられるようになってる。 短く書けるから習慣的にはバリアントに型を付けるほうが多いかな……? >>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, >>249 > Ok::<(),std::io::Error>(())とか 正直見たことなかったです >>252 > 歴史的経緯でバリアントにも付けられるようになってる。 (´・∀・`)ヘー そういうわけなんですね >>254 例えばlet x = spawn(async { ... });して裏で何か処理をやらせといた結果を 後でもしエラーが出ていたら進めちゃいけないタイミングでx.await?;で確認するわけだけど spawnが返す型をxに律儀に記述するのは面倒なのでそこは略すとして asyncブロック内で?とOk(())だけ書くとコンパイラが文句を言うので仕方なく記載 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; })();
ARM版のWindowsでRustのコードを書くのってめんどくさいな 生のWindowsで使用する場合、VisualStudioに付属するx86用のリンカーが非推奨警告を無視すれば使えたものの、次期バージョンからx64専用になってしまうっぽい 一方、仮想環境等+VSCodeでは、RustAnalyzer等が機能せず苦労する・・・・ 持ち運び用にケチってMacを買わなかったのが大問題だった、なんかいい方法ないのかな?そもそもARM Windowsで動くリンカーってVisualStudio付属のものしかないのかな? ケチったって言ってもよくよく考えてみると大して金が浮いてもないし、変なもん買っちまった
>>258 i686ターゲットのバイナリをバイナリ変換で動かしてるってこと? aarch64-windowsターゲットとARM用のリンカ使えばネイティブのARMバイナリできるんじゃないかと思うけど rustはどんどん新しい機能が追加されていくのはいいけど、 後方互換性を気にして過去に追加された「間違った」機能を削除するという 思い切ったこともやって欲しいな これまでの言語は後方互換性にとらわれて滅茶苦茶になってるから
実際2021editionではレンジパターンの ... が削除されて ..= に一本化された
>>258 今どきDocker使うのはデフォだから何の問題もない、因みにRustAnalyzerも使える 使えないのは、復数のプロジェクトが見える状態にしてるから 今どき復数ウィンドウ立ち上げても何の問題もない、ルートフォルダを固有にしたら解決 つまり、調査不足が原因 ARM は安物のイメージがある。 WSL2, Linux, Docker, VSCode とか使えるのかな? Mac も、M1 に変わったから
原理的には ARM のほうがインテル系 (もはや AMD 系と呼ぶべきか) よりも高速化できる可能性があるとは言われている。 今以上に回路を細かくするのは無理というところまできてしまっているのでアーキテクチャのほうで見直しが必要なんだが インテル系は互換性が足かせになってしまっていてあまり思い切ったことが出来ない。 現時点はインテル系向けにチューニングされたソフトウェア資産が多いから上手くいっているけど将来もそうとは限らない。
>>267 スレチだけどその話の出展あったら教えてほしい CISCRISCの話じゃなくてメモリモデルの話ならそうかなって思う 今のx64って実質中身RISCって聞いたことあるし Rustで言うとx64では全部SeqCstとして扱われるみたいな話
リソースの話だけなら命令デコーダーとか? x86_64は拡張や互換性とか可変長命令だったりで無駄にトランジスタ消費するのが電力性能比的に足かせみたいな
今どきのプロセッサだと分岐予測器やキャッシュが支配的でデコーダなんて誤差だと思う あと可変長は命令側の帯域やキャッシュ効率が良くなるという面もあってRISC系でも採用してることが多い 結局両者とも長年の改善で似たようなとこに落ち着いてるわけで、ISAが違うからどうこうみたいな話は結構眉唾
こういうことをしたいのですがコンパイルエラーとなってしまいます 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()); どう直せばよいでしょうか?
namaeじゃなくてnameな aが余計 この程度の英語のスペル書けないとかプログラミング向いていないしやめた方がいいよ
>>272 そのままだとarg1が消費されてなくなるのに参照だけ残ることになるからエラー Stringで受ければいいよ let name: String = arg1.unwrap_or(DEFAULT_NAME.to_string()); >>273 すげーな。してはいけないレビューの典型例じゃねーか。 すげーな >>275 これだとarg1がSomeの時もto_string()が呼び出されて無駄なヒープアロケーションが走るのでunwrap_or_elseにすべき みなさんありがとうございます Stringにする方法は無事にこれで動きました let name: String = arg1.unwrap_or_else(|| DEFAULT_NAME.to_string()); 元の質問>>272 のように&strにする方法は無いのでしょうか? もし可能ならばto_string()のヒープアロケーションを減らせるかなという質問です >>279 Cowかな let name: Cow<str> = arg1.map_or(Cow::Borrowed(DEFAULT_NAME), |s| Cow::Owned(s)); 場合によってはこれでもいいのかな? let name: &str = &arg1.as_ref().map_or(DEFAULT_NAME, |s| s.as_str());
>>281 それでもよいけど正解はシンプルなこれ let name: &str = if let Some(ref arg1) = arg1 { arg1 } else { DEFAULT_NAME }; まずarg1を消費しないようにrefで受ける 2代目のarg1は&Stringなので自動的に&strへderefされる 1.40からas_derefっつうのがあるんだってさ let name = arg1.as_deref().unwrap_or(DEFAULT_NAME); でもコマンドライン引数の場合は消費しないメリットがほぼ無いのでCowのほうがいいかな
どの型で統一すべきかは (1) その後に加工伸長などするならString (arg1をここで&strに統一するのは無駄) (2) 参照するのみなら&str (DEFAULT_NAMEをここでto_stringするのは無駄) (3) その後に判明する条件次第で両ケースありうるならCow (ただし常にCow利用はCowコストが無駄) って感じ?
おそらくその通りだけど、CLIツールの初回一回だけのアロケーションにそこまでこだわるのがそもそも無駄って気もする ループ内とかでもなければ雑にString作っちゃっていいかもね
>>285 今回のケースはそうだね ただしヒープ割り当てをなるべく避ける様々な手法を把握しているか否かは色んな局面で効いてくるから 今回6通りも動くコード例が示されたことは多様に対応可能な柔軟性の良さかな 気にしなくても書けるし気にすれば効率を上げることができる点で >>284 Cowと&strに揃える場合の一番の違いはライフタイム管理 Stringを&strにするとライフタイム管理がつきまとうから すぐ使いきる場合以外はCowに比べてメンテナンスしにくいコードになる 文字列についてはStringにするかCow<str>にするか迷うくらいならinternしちゃうのも手かと どのライブラリが定番なのかよく知らないけど
>>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); ken okabeのqiitaの記事がまた炎上してるよ mod_poppoにボコボコにされてる
あれ、Qiitaには垢バンされて投稿できないんじゃなかった?
ググってもよくわからないのですが、どういった方なんですか?
>>291 poppoとかいうやつも多様な定義や多様な解釈が存在している中で不要なイチャモンばかりだな さらに冒頭のこれも > JavaScriptで演算子オーバーロードを実現しようとするのは筋が悪い たまたま例としてJavaScriptを用いているだけなのにそれすら理解できていない okabeは使用言語と無関係に成り立つ話をしてるだろ > reduceは二項演算ではなく三項演算として捉えるべき これも些細なことであって例えばRustなら fold()は『入力列・初期値・演算関数』の三項演算だけど reduce()は『入力列・(初期値は入力列の先頭なので無指定)・演算関数』の二項演算 とはいえokabeの方もイテレータすら扱っていないからイマイチ P2P方式の2D対戦ゲームを作りたいと考えています。 おすすめのゲームエンジンやライブラリはございますか?
>>297 あくまでゲームを作ることが目的なんだったら普通にUnityとかでいいんじゃない? どうしてもRust使いたいならサーバー側で使えばいい >>298 個人的にRustが好きなので技術向上のためにもRustで作りたいと考えています。 現在はAmethystとlibp2pを用いて開発しようと考えているのですが、如何せん知識が浅くこれで目的のものが作れるのか分かりません。 是非先人の知恵をお貸しください。 Rustでの開発にロミオとジュリエットの恋ほどの壁があるという場合は、大人しくC++かUnityで作成します・・・ そんだけの情報ではなんともいえん。 見通しが立たないものを試行錯誤で作る場合には モジュールではなくレイヤで分割したほうがいいという考え方がある。 要するに機能不足でもバグだらけでもコードが整理されてなくてもいいからとにかく「動くもの」を作って その上に足りないものをどんどん足していくという方法論だ。 よくわかってないなら小さいもので色々やってみて知識を積み重ねるべきで、 よくわからんまま目的に向かって邁進してもあんまり技術向上にはならんよ。
>>300 ありがとうございます 色々試してみます >>299 Amethystは開発中止になったから今からやるのは微妙かも gamedev.rsに今アクティブなエンジンやゲームがスクショ付きで載ってるから そこからイメージにあうものを探すといいかもしれない >>302 そうだったんですね・・・ 時間は掛かるかもしれませんが、色んなものを試して自分に合う物を探すことにします 技術を高める目的なら windows-rs や wgpu-rs みたいな一段下から積み上げるのも楽しいよ。 ゲーム作る道のりは遠くなるけど、画面に三角形出したり、キーの入力受け付けたりするだけで達成感が出てくる。
既出だったらすいません 前から疑問だったのですが、出力におけるprint!マクロのような入力用マクロが標準ライブラリに用意されていない理由ってなんですか?
>>297 1対多で、broadcast するチャットルームのバックエンドなら、 Ruby on Rails 6 のAction Cable(WebSocket)が基本 JavaScript は、React, Phaser とか 【Rails】(送信時のリロード無し!)Action CableでSlack風チャットアプリを作成、2019/12 VIDEO >>308 熟読させていただきました。 私としてはたった数十行のコードであること且つ他のほとんどの言語に存在しているものなのであっても良いのかなと思ったのですが、Rustの基本理念を考えると慎重になる理由も理解出来ました。 Rustの経験が浅い私目線ではあまり腑に落ちませんでしたが、皆さんはどう考えていますか? 熟読はしてないけど、外部ライブラリで簡単に実現可能であるなら 直感的には、本体メンテナの苦労を増やすほど価値があると思えないし別にいらんかな こういうIOとかCLIプロンプトらへんの機能ってどうしても好みが分かれるというか、 ユースケースによって必要な機能がかなり違ってきちゃうと思うし
C++のiostreamが失敗作だから慎重になるのもわかる笑
>>306 C言語でのprintf相当はあるのにscanf相当がstdにないのはなぜか?ですね 逆になぜprintf相当がRustの標準ライブラリにあるのか?を考えてみると (1) 読み書きの非対称性 人間が読むためにプログラムが書くことはエラー表示を含めて利用必須かつ頻出 一方で人間が書いたものをプログラムが読むことはレア ファイルや通信相手への読み書きは各プロトコル/各API/シリアライズ等で対象外 (2) コンパイラサポート print!やformat!等は頻出するので効率面からコンパイラサポートが効率的 実際にそれらが利用しているformat_args!はコンパイラ内蔵マクロ (3) 標準ライブラリ採用 外部ライブラリで実現可能なものは採用しないが基本 今回はコンパイラ内蔵マクロだから採用 つまり出力は頻度と効率化で採用がクリアされたけど 入力は外部ライブラリで十分かなと しかし競プロでみんなproconioとかいう謎の専用ライブラリ使ってるの見た目悪すぎて笑える
競プロ全然知らないけどstdinに入力数値がくるからか | 入力は以下の形式で標準入力から数値が与えられる。 | a b c d e | 積が奇数なら Odd と、 偶数なら Even と出力せよ。 標準ライブラリだけ使うと毎回こんなの書くのは面倒だもんな | use std::io::{stdin, BufRead, BufReader}; | println!("{}", if BufReader::new(stdin()).lines().next().unwrap().unwrap().split(' ').any(|s| s.parse::<isize>().unwrap() & 1 == 0) { "Even" } else { "Odd" }); >>313 そのproconioを使うとこうなるようだ | proconio::input! { v: [isize; 5] } | println!("{}", if v.into_iter().any(|n| n & 1 == 0) { "Even" } else { "Odd" }); >>315 マルチラインだと標準ライブラリだけでもわかりやすくなる? >>316 あいやごめん。proconioのところはどうあがいてもプロコン専用のproconioが一番見やすいよ。それ用に特化されたライブラリだし 俺が大好きかよって言ったのは println!("{}", if v.into_iter().any(|n| n & 1 == 0) { "Even" } else { "Odd" }); の部分 どの言語でも三項演算子(相当)はワンライナーで書くんじゃね?
これでいいかね? println!("{}", if v.into_iter().any(|n| n & 1 == 0) { "Even" } else { "Odd" });
俺ならこんな感じかなあ 俺は頭悪いから、途中結果に一つ一つ名前つけないとわかんなくなっちゃうわ まあワンライナー見た時に「グエー」って思っただけだからごめん。「大好きかよ」とかいっておいてなんだけどあんま気にしないで use proconio::input; fn main() { input!(v: [usize; 5]); let is_even = v.iter().any(|x| x % 2 == 0); let result = if is_even { "Even" } else { "Odd" }; println!("{}", result); }
>>321 なるほど じゃあ最初の標準ライブラリのみはどのように分けるのかな println!("{}", if BufReader::new(stdin()).lines().next().unwrap().unwrap().split(' ').any(|s| s.parse::<isize>().unwrap() & 1 == 0) { "Even" } else { "Odd" }); >>322 proconio使うw Rustで競プロするにはIOごときに専用ライブラリ使うことになってなんかなあって思うけど、そう思いながら使う >>315 ワンライナーやクロージャでまとめて書くと所有権やライフタイムの問題が出にくいんだよ 他の言語と違ってRustで適切に分割して読みやすく書くのは初心者には難しい わけわからんくてビビってたらよく見るとPythonスレじゃなかった
>>326 println全体が改行されてしまった println!( "{}", if BufReader::new(stdin()) .lines() .next() .unwrap() .unwrap() .split(' ') .any(|s| s.parse::<isize>().unwrap() & 1 == 0) { "Even" } else { "Odd" } ); 一方で分割するとこうなった let cond = BufReader::new(stdin()) .lines() .next() .unwrap() .unwrap() .split(' ') .any(|s| s.parse::<isize>().unwrap() & 1 == 0); println!("{}", if cond { "Even" } else { "Odd" }); if式自体はワンライナーーで正解とrustfmtはおっしゃってる 例えば>>321 がif式を5行に分割しているが それもrustfmtにより1行に修正された Rust標準ライブラリのソースでもワンライナー bool.rs: if self { Some(t) } else { None } result.rs: let n = if self.inner.is_some() { 1 } else { 0 }; time.rs: let prefix = if f.sign_plus() { "+" } else { "" }; >>328 では何の話かね? 心底どうでもいい。 保守性に貢献するどころかマイナスになるようなクソプライドコードの導入すんなよ。
区別できなかったからrustfmtを持ち出したんやろ どこに改行入れるべきかって話だと思ったんだろなww
rustfmtを持ち出したのは俺じゃないぜ あと今回のケースならここは分割するよな let reader = BufReader::new(stdin()); let line = reader.lines().next().unwrap().unwrap(); let is_even = line.split(' ').any(|s| s.parse::<isize>().unwrap() & 1 == 0);
問題文が>>314 でこうなってるから >> | 入力は以下の形式で標準入力から数値が与えられる。 >> | a b c d e if let [a, b, c, d, e] = line .splitn(5, ' ') .map(|s| s.parse::<isize>().unwrap()) .collect::<ArrayVec<_, 5>>()[..] { あとこれでいい let is_even = a & b & c & d & e & 1 == 0; そういえばいつの間にか let variable = 3; println!("{variable}"); みたいな書き方が出来るんだけどこれ前からだっけ?
>>338 それはまだnightlyだけですよ 来月の1.58からstableになって使えるようになる予定 間違っているコードは出ていないような 問題視してる人は具体的に何を問題にしているの?
>>345 ガード構文がこう「『Pattern』 if 『Expression』」なのでそうなりますね > しかし競プロでみんなproconioとかいう謎の専用ライブラリ使ってるの見た目悪すぎて笑える いや、ほぼ公認のクレートなんだが・・・ 謎の専用ライブラリとかいってる時点で、お察しか? あと、見た目悪いってどういうことなんかね includeしたら見た目悪すぎなわけ?
Rust bookの以下の記述について質問です https://doc.rust-jp.rs/book-ja/ch19-05-advanced-functions-and-closures.html# クロージャを返却する > 以下のコードは、クロージャを直接返そうとしていますが、コンパイルできません: > > fn returns_closure() -> Fn(i32) -> i32 { > |x| x + 1 > } > コンパイラには、クロージャを格納するのに必要なスペースがどれくらいかわからないのです。 > この問題の解決策は先ほど見かけました。 > > fn returns_closure() -> Box<Fn(i32) -> i32> { > Box::new(|x| x + 1) > } とBox化しなさいと書かれているのですが 以下のようにimplを付けるとBoxを使わなくてもコンパイルが通り動きました fn make_closure_add1() -> impl Fn(i32) -> i32 { |x| x + 1 } このimpl付加は暗に自動的にBox化されているということなのでしょうか? >>348 そりゃあcratesio に上がったものは空っぽでもゴミでも全部公認だわなw >>342 逆だ、コードは常に「正解」で動いていて「間違い」ない。 あんたの書いたバグもコンピューターにとっては一部の隙もなく正解であり、書かれたその通りに動き、無慈悲である。 同じ目的で、多数、あるいは二人の人が書いたコードで違いが出るのは「表現の違い」であり「間違い」と決めつける のは人間の主観や嗜好でしかなく、仮にコードへ正誤を求めるなら明確に表現できていなればならず、矛盾が生じる ”動いていて”と言っているからコンパイルは通ってる前提だろう、”コードへ正誤を求める”といっているから 仮にコンパイルが通らないコードは明確にそれ(誤り・間違い)が表現できている ポエミーなのはその通りだろう
>>349 Boxとはヒープを使うということです Rustではコードで明示的に指定しない限り勝手にヒープが使われることはないです (もちろんBox以外にもVecやStringなどヒープを使うものを使ってもそれは明示的に指定したことになります) その Box<Fn(i32) -> i32> は今は Box<dyn Fn(i32) -> i32> と書く必要があります では本題の impl Fn(i32) -> i32 と書いた場合はどうなるのでしょうか? 以下のように3種類のクロージャを作ってサイズや型を表示させてみると fn main() { let direct_closure = |x: i32| x + 1; let impl_closure = make_impl_closure(); let box_closure = make_box_closure(); println!("{} {}", std::mem::size_of_val(&direct_closure), type_of(&direct_closure)); println!("{} {}", std::mem::size_of_val(&impl_closure), type_of(&impl_closure)); println!("{} {}", std::mem::size_of_val(&box_closure), type_of(&box_closure)); } fn make_impl_closure() -> impl Fn(i32) -> i32 { |x| x + 1 } fn make_box_closure() -> Box<dyn Fn(i32) -> i32> { Box::new(|x| x + 1) } fn type_of<T>(_: &T) -> &'static str { std::any::type_name::<T>() } 実行結果は以下のように表示されます 0 tmp::main::{{closure}} 0 tmp::make_impl_closure::{{closure}} 16 alloc::boxed::Box<dyn core::ops::function::Fn<(i32,)>+Output = i32> つまりimplでは直接クロージャ指定したのと全く同じです (上記では定義した関数名だけが異なる) では常に impl を使えばよいのかというと 以下のような条件によって異なるクロージャを返す時 ここで Box を使わず impl Fn(i32) -> i32 にしようとすると 2つのクロージャの型が違うとコンパイラに怒られます fn make_closure(curry: Option<i32>) -> Box<dyn Fn(i32) -> i32> { if let Some(curry) = curry { Box::new(move |x| x + curry) } else { Box::new(|x| x + 1) } } 結局クロージャでない場合と同じ話で 同じトレイトでも型が異なるものが同居する時にBox化します >>349 のRust bookの例はBox化が不要なケースでBox化だから混乱しますね コンパイル通ってれば全て正解とかバカ丸出し。 厳密な定義でも使えない定義があるってことすら理解してなさそう。
型安全だったらコンパイル通れば実行時エラーにならないという点で全て正解っていうのは別に間違ってないと思うけど? これにケチつけるのは流石にどうかと
バカ丸出しにお前バカだろとわざわさ言うのもバカなんじゃなかろうか
>>360 実行時エラーにならないなんて最低限のところだっつーの。だからバカだっていうんだよ。 もしかしてrustはlinuxに取り込まれるわけねーだろって言い張っていた人? 予言外していたよね。お疲れ様です。
バカをスルーできないバカっているよねー >>364 お前の事な 25 デフォルトの名無しさん sage 2021/04/27(火) 08:00:23.09 ID:/+bIFNU8 >>23 あのね。。書けばそうなるってものじゃなくてそれを実装しなきゃならんのよ。。 コンパイラにそういったコンテクストを判断させるのがめちゃくちゃ難しいっていってるでしょ? なんでそんなに読み取れないの? 27 デフォルトの名無しさん sage 2021/04/27(火) 16:10:45.63 ID:/+bIFNU8 >>26 だからそのコードじゃpanic捉えきれねーからカーネルに入れるわけねーだろって 言ってんじゃん。。何読んでんだよ。 28 デフォルトの名無しさん sage 2021/04/27(火) 18:23:48.67 ID:n/AWrch2 まあ半年後どうなるかで誰が正しかったかは分かるわな 29 デフォルトの名無しさん sage 2021/04/27(火) 20:32:29.92 ID:/+bIFNU8 半年も経たなくてももうわかってるっつーの。。だからちゃんと英語の勉強しましょうね。 完全に同一人物だよね 予想が完全に外れたID:8qqh3vKrを晒し上げ♪♪♪ ここまで簡単な予想を外すとかバカ過ぎて生きていけなさそうwww 馬鹿丸出しですねwwwwww
素でバカなんだな。。もうコンパイル通ったんで俺の仕事終わりとか現場で言ってろよ。。話にもならん。
>>362 最低限の性質を満たしている⇔正解って言ってんじゃん。。何読んでんだよ。 なんでそんなに読み取れないの? だからバカだっていうんだよ。 だからちゃんと日本語の勉強しましょうね。 >>369 なお予言を外したことについては一貫してノータッチwwwww 話をしたくないのは君だよねwwwwww >>369 同一人物だってことはバレバレだっつーの。バカ丸出し。wwwwwwwwww 厳密な定義でも使えない定義?Rustに特定条件下でCのような未定義になる動作あったっけ?
スレの文脈はしらんけど、 Rustではunsafeを使ってなければコンパイラが、未定義動作が起きないということや、データ競合がないことを保証をしてくれるよ
Rustの勉強を昨日から開始した。後は構造体とかかな。
コード貼ったら糞だボケだゴミだと自称上級者に罵倒されるから注意しろ
Rustはこう謳っている >なぜRustか? >パフォーマンス >信頼性 >生産性 真っ向から反するコードを貼ってりゃゴミ・クソ言われて当然なんだよなぁ
すまんが、配列に入った数値の平均ってパッと出せないもんなの? 他言語でふにゃふにゃになった俺の頭でコードを書いたら、桁の溢れとか精度とか酷えことになりそう・・・・
コードもゴミだったがそれ以上に考え方がゴミだったからな
>>381 普通に平均を求めるだけではダメなのでしょうか? fn main() { assert_eq!(5.5, (1..=10).average()); assert_eq!(6.8, [2.3, 8.7, 9.4].average()); } use num::ToPrimitive; trait Average { fn average(self) -> f64; } impl<I> Average for I where I: IntoIterator, <I as IntoIterator>::Item: ToPrimitive, { fn average(self: I) -> f64 { self.into_iter().fold((0.0, 1.0), |(ave, size), n| (ave + (n.to_f64().unwrap() - ave) / size, size + 1.0)).0 } } ここの人たちってplaygroundとかなんで完全に動かせるコードで提示しないんだろ・・・? アドバイス貰うにも回答するにも一生懸命スペース全角置換したり、まじ両方キモイw trait Averagewwwww
普通の関数にすべきかどうかはメソッドチェーンにしたいかどうかで判断すればよろしい
>>389 playgroundでは自己顕示欲が満たせないんだよw まあplaygroundでは動かせないコードもあるけどな >>381 こういう子は、移動平均出したくなった時とかどうすんだろ… 愚直に毎回平均出す関数とか使っちゃうわけ? >>384 それだと桁溢れは防止できているが誤差蓄積の対処ができていない もう一つパラメタを増やしてこうしたほうがいい fn average(self: I) -> f64 { self.into_iter().fold((0.0, 1.0, 0.0), |(ave, size, fix), n| { let diff = (n.to_f64().unwrap() - ave) / size - fix; let new_ave = ave + diff; (new_ave, size + 1.0, (new_ave - ave) - diff) }).0 } >>387 イテレータメソッド化するにはそのためのtrait宣言が必須 もしわからないならitertoolsなどのイテレータ拡張ライブラリを見よう >>389 標準ライブラリのsum()がtrait Sumを使っているからtrait Averageでもまあいいとは思う ただし今回はイテレータメソッド拡張のみに用いているようだからtrait IteratorExtなどの命名がわかりやすいとは思う IteratorExt大草原、まじに入院してほしいw
>>398 標準ライブラリにおいてsum()やproduct() それを一般化したfold()やreduce() さらにmax()やmin()など当然イテレータメソッドになっている むしろ今回のaverage()だけをイテレータメソッドにしない理由が見当たらない >>400 入力型と出力型で大量の組み合わせ(例:i32→f32)が用途に応じて要求されるのと 単純に合計をサイズで割った平均でよい用途もあれば 件数が多いと合計がオーバーフローするからその対策が欲しい用途もあれば 桁が大きく異なるデータ列の場合に浮動小数点の誤差改善が欲しい用途など多岐にわたる だから平均を標準ライブラリで何か一つ用意は無理 単にこれまで標準ライブラリに入れようとした人がいなかったか そういう人はいたが必要性を説得できなかっただけでしょう sumやproductに比べるとユースケース限られるしね
「件数が多いと合計がオーバーフローするからその対策が欲しい用途」そんな考えがオカシイ sumですらオーバーフローに言及しているだけで対策が欲しいから、だからstdじゃないという理由では無い https://doc.rust-lang.org/std/iter/trait.Iterator.html#panics-3 When calling sum() and a primitive integer type is being returned, this method will panic if the computation overflows and debug assertions are enabled. 必要性を説得出来ないだけというのが正しい。浮動小数の加減算による誤差蓄積だって、浮動小数を扱うなら当然起こることだが 誤差改善が欲しい用途があるからstdじゃないとか嘘ばっかり言わないで?おまえさ、迷惑だからRust辞めてくれよ? 最小限、分かったふりで糞まき散らすな? >>405 それは君が無知 平均算出にはsumを求めずとも差分を逐次的に適用するアルゴリズムがあるoverflowを回避する対策で一般的に使われている 例えば>>383 のstatsもその方法で平均を算出している sumの例を出すのは見当違い >>406 アルゴリズムの話じゃなくてstdに入ってない理由の説明がおかしいという指摘だと思うよ 言葉汚いし何言ってるかわかりづらいけど >>407 stdに入ってない理由? 一長一短ある複数のアルゴリズムがあるから外部でいいだろう >>408 その理屈だとsortも該当するが そもそも前提としてaverageをstd >>409 途中で書き込んでしまった そもそもaverageか類似の関数をstdに取り込む議論が過去にあったならそれをポイントしてほしい 多数あり外部で十分派なのでそこは興味ない sortは2種類しかなく2種類とも標準ライブラリでサポートしている
>>411 じゃ一番メジャーな外部ライブラリを教えてよ 外部で十分と言えば、cratesは先着順で名前取れるからこの先優良ライブラリ程クソみたいな名前になっていくよね
>>413 マジでこれ いい名前取るだけ取って何年も放置とか多すぎ なんかいい方法無いかねぇ… ライブラリの永続性を保つのに名前でマッチさせるのやめてUUIDかハッシュか何かでマッチさせれば良かったのにみたいな気持ちはある
>>414 横から失礼。 個人的にはそもそもcrate.io に頼るのがあまり好きでないなと。 Rust に限らない話だけど、こういった中央集権的なリポジトリを用意すること自体が名前争奪戦の元になるのではないかなと。 それぞれのWEBサイトで勝手に配布すればいいのにってね。 バージョン管理方法の方法論なんかも関わるから簡単な事ではないんだろうけど。 現状でもGitHubから直接落としてくることも出来るけど、GotHubのリポジトリは消せてしまうからな
cargo updateでマイナーバージョンアップやらせるのも規約決めたり工夫ご必要だし 専用のリポジトリ用意する方がわかりやすくはあるような crates.io以外にも複数のリポジトリを混在して使えるようにできれば良いのかな
cargoはどこからでも落とせる crates.ioなみに安全性、信頼性、永続性が確保できるんなら好きにすればいい
永続性を謳うサービスで名前で管理したらそりゃあいい名前の取り合いになるよなあ
GoみたいにGithubがデファクトスタンダードなレジストリになっても一覧性の面で不便だし、crates.ioでいいと思うけどなあ
セキュリティ監査付きのcrates.ioクローンが欲しい
average()が気になってcratesだかcargoだか話逸らしに聞こえる
ぼぼぼ、ぼくちんのために誰か優秀なaverage()をおながいします
Why is my Rust build so slow?
Because your PC is poor spec.
>>432 bin crateのビルドに時間かかるなら sccache は効果薄いのでは 高度IT人材、富士通は最大年収3500万円へ 「富士通年収3500万!」日本のIT企業の年収も、高額化してきました AI人材の獲得に超本気 NECが新人事制度を9人に適用、富士通は最大年収3500万円へ 【年収3500万円も】富士通、「ジョブ型」人事制度を導入 幹部社員から 高度IT人材 来年度から副業解禁 人材多様化へ―大同生命次期社長 第一生命HD、副業解禁 約1万5000人対象 第一生命HD、副業解禁 1万5000人対象―大手生保初 IHI、国内8000人の副業解禁 重厚長大企業も転機 IHI、社外兼業を解禁 社内副業もルール化
効率を求め過ぎてモノリシックになりすぎると様々なコストが上昇してしまう そこで分割 さらに内部もcrate分割で並行コンパイル
Rustでノードが追加されたり消されたりする双方向グラフ扱いたくなったらどうするんだろ Arenaじゃ追加削除してるうちにゴミがメモリ圧迫していくし
>>437 Cursor使う Arenaの場合はfree list用意して削除済み要素を再利用すればよい Arenaが埋まったらreallocするのではなく同じサイズの新たなArenaを獲得していくようにすれば 不要になったArenaから解放できるからmalloc使う場合と同等のメモリ使用量に抑えられるかと これでも不足するなら copy GC 的に compaction するしかなさそう >>438 なるほど…… 結構ガッツリ実装しなきゃいけなそうね。ありがとう。最後はGCを実装することになりそうなので、それならいい感じのGCを残しておいてくれたら良かったのにって気になるな >>439 GCを必要とする用途は非常にレア だから標準ライブラリには不要だが外部ライブラリに色々あるので大丈夫 >>442 GCのライブラリ使ったことある?実用的だった? 特殊な案件でしかGCを使うことはないため その場合は汎用GCライブラリ利用よりも データ構造とアロケーションとGCを密に設計する方がベターかも
ちゃんとしたスマートポインタを自作する時点で結構大変だから用途特化した方が確かによさそう
グラフみたいなデータ構造を実装するだけでも、GCが必要になったりするもんなん?
グラフ構造表現するだけならArenaや、少し安全にするならGenerationalArenaで事足りるかと copy GC的なものが必要になるのは大量にノードを作成してほとんど削除、一部残存みたいな状況でfreeできないArenaが残ってしまうケース
ちゃんとしたGCはないのに、参照カウントだけは標準ライブラリに入っていて循環参照には気をつけましょうねーって運用でカバーなのはなんか中途半端なものを感じる そもそも参照カウントなんてGCとしてはかなりイケてない部類なのになんで参照カウントなんだ気持ちもある
単純にRcやArcはGCを目的としたものではないから
単なるスマートポインタをGCと言っちゃうあたり・・・ あれは単にRAIIでヒープを処理してるだけのことであって わざわざGCと呼ぶような大したもんではない
9分で轟沈したのに2時間後に死体蹴りせんでも。 LLVMにレジスタとスタック使わないよう教える術がないし rustは意地でもスタックに置きたがるしplacement系が削除されたから保守的になるよね。 実際、保守的gcってどれくらい回収できんの?
>>449 即時解放がやりやすい&実装が簡単だから、だろ。 そういう説明をしないで>>452 とか言うやつはRust普及の足を引っ張っているだけだから、書き込みしないほうがいいと思う。 >>454 >>450 の言う通りrustのreference counting gcは メモリ管理のためではなく共有された参照を数えるためのもので シングルスレッド用のRcがあるのはrustがaffine typeだから 共有された可変を認めないからで、ついでに>>452 の言うことも半分あってるよ。 rustは自動参照カウントにRAII併用するけどgcのない言語しか経験がない人が gcをスマートポインタと混同するのもよくある事。 あと、rustの参照カウンタは弱参照があるから循環参照が切れる代わりに 単純な参照カウンタのオーバーヘッドが少ない・開放されるタイミングが 予測可能というメリットはないからrustが参照カウンタを用意する メリットは>>450 が指摘したものしか無いよ。 学問的には参照カウントはGCの一方式として分類されるのが普通だよ まあなんの前置きもなくGCといったらトレーシングGCがイメージされるというのもその通りだが RcがGCだと言っても間違ってるということはない RustのstdにトレーシングGCがないのは、単に標準ライブラリを大きくしない方針に従ってるだけじゃない? 本格的なGCが必要なケースは限られるし、外部クレートで十分と思うが
>>457 コテハンつけるかID変えないならまだいいんだけどな 気持ち悪さは長文から来る。reference counting gcとかわざわざ参照カウントを英文で書いてカッコつける所も 減点項目。個人の主観的には文中に>>を挟む特徴が読み手の事を一切考えないオナニーに見える
文中に >> 挟むのだめなのか どのレスのこと指してるのか明確になって良いと思うが
>>レス番 ↑これで引用先に飛べるリンクが張られるの知らなさそう 5chにPCからアクセスしたり 専用ブラウザからアクセスしたときそうなってるのよ
Rustのメモリ安全性ってどうやって保証してんの? テスト?
ダメというわけじゃないが、同じ人が言ってるわけじゃないのに文中に一つにまとめて自分の考えだけを 長々と話している時点で意味わからん、リンクの話じゃない。気持ち悪さがどこからくるかという話。 感想といえばその通りで、個人的な主観と言ってるが多くの人はそう感じるのはこうではないか?という話
>>465 Rustスレで学級会始めるおまえも気持ち悪いよ >>465 よくわかる いつも中身ないので即NG だがやつは自演魔なんでマジタチ悪い >>469 がいつものキチガイの典型例 文句をつける意味不明な書き込みをしてその後に自演で同意のレスを付けてくる スレを荒らすことが目的 このスレ常駐の荒らしは以下の特徴があるから無視すればよい 「気持ち悪」「ゲロ」「汚」などの言葉を好む 別案・別情報・別解釈などを具体的に出せず文句を付けるだけ そのような無意味な書き込みになぜか賛同レス
>>463 コンパイル時にチェックされるものと実行時にチェックされるものがあるよ 多くの場合は前者で済むけど、可変参照を複数箇所で共有したい場合は後者が必要になる >>475 コンパイル時のチェックって結局人手で書いたコードによるチェックなんでしょ? ということは担保は単体テストってこと? そもそもだかマシン語でみりゃ メモリに型もなんもないよw 単なるバイトだらけ コンパイルの段階で変なコードか けないようにしてるだけ でもこれだとぬるぽ!が回避できないので 仕方なく仕組み入れたのがRustでしょ
Rust書くときは常にIDEに直してもらいながら書いてるから>>476 みたいなの全然解けないわw >>476 確かにこれは勉強になる 知らなかったことばっかり Rustのメモリ安全って、null安全のことだったの?しょぼ
>>486 初心者向けにわかりやすく解説しようという試みは評価するが 間違いが多すぎて萎える ここで初心者の質問に回答してる初心者と同じ >>488 整数型や浮動小数点型といったスカラー型の変数は、変数間の代入において所有権は基本的に複製されます。これを「所有権の複製(コピー)」といいます。変数間の代入などにおいて所有権は複製されるので、値の所有者は常に1個でなければならないというルールは守られます。 >>489 正しいこと言ってるように見えるけど どう間違ってるの? 所有権が複製されるって表現はなんかRcを想像してしまうな
整数型とかの単純のヤツはCopyだかCloneだかをderiveしててデフォのmove semantics機能してなかった気がするな 最近書いてねぇから忘れてんなこれ(´・ω・`)
数値型が特別扱いされているわけではない Copy traitを実装すればcopyされる Copy traitを実装しなければmoveされる
Copyで複製されるのは所有権じゃなくて値だよね 代入先の変数が複製された新しい値の所有権を持つことになるけど、所有権が複製されるんじゃあない
copyだけでなくmoveされるのも対象は値 所有権ではない 値をmoveした結果 値に紐付く所有権がくっついてくる
値と同時に所有権 "も" copy/moveされると捉えることもできるのでは
個人的に間違った捉え方をするのは自由だが それを初心者に広めるのはやめていただきたい
あんたらのお仲間他スレで暴れてばかりだよ、引き取りに来い
所有権が複製できちゃったら「一個の値に一個の所有者」ってルールが守れないやん(´・ω・`)
というかCあたりの関数の引数も コピーじゃなかった? 中身を渡した先でもいじらせる場合とかは ポインター渡しとかなんかやってた記憶
Cでポインタ型の値を渡してるだけのことを 参照渡しと言っちゃう害悪がネットにチラホラ残ってて悲しい ポインタ渡しとかいうすっとんきょうな用語も必要性を感じない Javaで単に参照型変数の値を渡してるだけのことを 参照渡しと言っちゃうのも割りとあって悲しい
>>501 値が複製された場合なら問題ないんでないの? Rustは非常にシンプルで Copy traitが実装されていない型は値と所有権がmoveされる Cooy traitが実装されている型は値がcopyされてその値に新たな所有権が生じる もちろんそれを所有権と値が複製されたとみてもよい ちなみにRcは所有権の複製ではなく所有権の共有
「所有権とは、文字通り変数が値を所有できる権利のことです。」 ふむふむ 「スカラー型の変数は、変数間の代入において所有権は基本的に複製されます。これを「所有権の複製(コピー)」といいます。」 ふむふむ・・・ 「変数間の代入などにおいて所有権は複製されるので、値の所有者は常に1個でなければならないというルールは守られます。」 ・・・は? 元の値を所有できる権利を複製したんじゃなかったの?
>>511 所有権と値は必ずセットで複製される 当たり前だが片方だけが複製されることはありえない 必ず1対1の関係になる ちなみにRcの場合は所有権は複製されずに所有権の共有となる >>511 >変数が値を所有できる権利のこと "所有できる権利"という捉え方が良くないね >>489 所有権だけが複製されるわけじゃないし、たしかに不自然だな >>509 複製された(結果的に値は同じだけどメモリ上の位置は異なる)新たな値に対する新たな所有権を「所有権が複製される」って表現するのはおかしいべ? >>510 そこよく勘違いされてるけど値自体はCopyトレイトと関係なく常に複製されてるよ(最適化で除かれるとかは別として) https://doc.rust-lang.org/std/marker/trait.Copy.html に全部書いてあるけど Copyな型は「プリミティブ型みたいに単純にメモリの内容を複製するだけでオッケー=copy」 非Copyな型は「ポインタとか含まれてたりして単純にメモリの内容を複製するだけだとNGな可能性があるから古い方は使えなくするよ=move」 ってだけ >>515 ルールを守れるか守れないかという話。 表現としておかしいかどうかは結論なんて出せないんじゃね?公式ドキュメントででも謳ってない限り。 著者のバックグラウンド見る限りC/C++の経験があるオレならRustも分かっちゃうよ?みたいな連載だから表現が慣れてなくても気にしない 監修もRustの人っぽくなさそうだし…多分、関数型の語彙が無いんじゃないかね
うんこ言語を好意的に連載してる記事をこき下ろす性格の悪いRusterが集まる駄スレ
>>518 だよなぁ。 誰か筆者に指摘した? あと、「所有権の複製(コピー)」はRustだと実際は何て言ってるの? 「所有権の複製」でググってもほぼ誰も使って無い用語で草
所有権だけをすることなんてないだろうし、そんな言葉なくね そもそも意味わからん 処理系を実装してる人にとっては考えることかもしれんが
まあ無駄にややこしいだけの説明だわな。 これで間違ってない!とか意地張るような輩は俺だったら落とすわ。
どっちに解釈しても問題ない話の一方に固執する方がお断りだな。
所有権の複製の意味が分からん 不用意に独自用語使うのを野放しにしちゃいけない 専門用語とその定義がなぜあるのかを考えて欲しい
>>519 実際に生ポインタ(アドレス)を見る形で実験したところ 「関数に値渡し(≠参照渡し)する」時は 「Copy trait実装の有無」に関係なく 「必ず値は複製されている(=別アドレスになる)」という よく考えれば当たり前の挙動となった つまり話をまとめると ・Copy trait実装の有無と関係なく「値は必ず複製される」 ・Copy trait実装がある時は「所有権も複製される」 ・Copy trait実装がない時は「所有権は移動する」 これ以外に表現しようがないことがわかった メモリ上の動きと言語上のルールの違いもまともに理解してなさそうな連中だな。
mutの時に書き換えると元の値と別の値に出来るのだから新たな所有権が生じているのは事実 あとは、所有権の複製、という表現の問題 間違ってはいないが理解しにくいならば別の良い表現で置き換えればよいが何がいいだろう?
ムーブの場合は元の所有権が破棄されて新たな所有権が生成される
値を複製したらメモリの領域が別なのに「所有権の複製」は何を複製すると思ってんだろ
>>532 さっぱりわからんよな >>529 > 間違ってはいないが理解しにくいならば 何言ってんのコイツ? 全く同じものを新しく生成するのと複製は外から見て区別しようがないんだから
所有権はコンパイル時のはなし メモリの動きは実行時のははし
現実世界の所有権が複製可能な権利だったのなら 複製されると勘違いする人がいても不思議はないが・・・
質量保存則に縛られる現実世界の複製との対比には限界があるわな。
権利のような無形物は質量保存則には縛られない Borrow Checkerが課すルールをわかりやすく説明するためのメタファーとして 現実世界の所有権という概念を借りてきてるのに そのメタファーを分かりにくくするエクストリームなルールを勝手に追加して広めるのはやめよう
というかRustの実装のところ見ればいいんじゃね? 仕組みのコードはあるだろうし
Ownershipを所有権と訳したから 「所有権とは、文字通り変数が値を所有できる権利のことです」 という間違った解釈がされて さらには「所有権の複製」なんていうトンデモ説が出現しちゃう
>>543 ownershipが所有権ならtransferは譲渡 move(移動)とは明確に使い分けられてる 結局はどこにも原典がないオレオレ用語でワロタ 技術は研鑽していかないといけないのに ポエムと独自解釈と拡大解釈によって逆方向に突き進むボンクラ
まぁ、オープンソースですらねぇし専門家の目すら通ってない様なサイトだし それっぽい事書いときゃいいんだよってのが大抵のweb界隈 itで何か知りたいなら手っ取り早い話公式ドキュメント当たれって事だろ(´・ω・`)
それでわかりやすくなるんなら別にいいけど、余計わかりにくくしてるってさぁ。。
ルー語で言うと「ownershipがtransferされることをmoveって呼ぶ」的な?
this is known as a move. 英語のほうだと「ムーブとして知られています」か 実際コンパイラのソースみてもownershipはtransferとかtakeっていう表現でmoveだのcopyとは言ってないね
>>549 公式リファレンスくらいは英語で読もうよ そんな変なサイト見てるからOwnershipの意味すら取れなくなるんだぞ >>552 それは変なサイトではなくRust公式ページの日本語訳。 元のRust公式ページでも同様。 Ownership and moves https://doc.rust-lang.org/rust-by-example/scope/move.html Because variables are in charge of freeing their own resources, resources can only have one owner. This also prevents resources from being freed more than once. Note that not all variables own resources (e.g. references) When doing assignments (let x = y) or passing function arguments by value (foo(x)), the ownership of the resources is transferred. In Rust-speak, this is known as a move. >>553 Google翻訳にかけてごらんw 主語も目的語も受動態の意味もわかってないような翻訳はゴミ DeepL で翻訳してみた なぜなら、変数は自身のリソースを解放する役割を担っているからです。 リソースは一人のオーナーしか持つことができません。 これはまた、リソースが複数回解放されるのを防ぐためでもあります。 すべての変数がリソースを所有するわけではないことに注意してください(例:参照)。 代入(let x = y)や関数の引数を値で渡す場合(foo(x))。 リソースの所有権は移転する。 Rustの用語では、これを「移動」と呼びます。
「所有権の複製」とか、意味の分からない事を書いている、香具師がいるのか?
>>556 !Copyの場合は所有権がtransferされるのであれば Copyの場合はどう表現すべきかという話 >>557 元の値の所有権はそのまま、複製された新しい値の所有権が新しく発生する。 元の値の所有権について何も操作は行われておらず、表現すべきことも無い。 所有権が複製されるという書き方よりも、むしろ、 primitive を含むコピー型変数は、コピーされると、 所有権は移動しないので、元の変数にもアクセスできる、みたいに使う コピーされると新たに、別のオブジェクト(実体・メモリ領域)と所有権が作られるとか
- 所有権ごと複製するという表現がわかりやすいかわかりにくいか⇒人それぞれ - 所有権を複製するという表現が逆になにか問題を生ずるか⇒今のところ挙げられてない
>>556 そう 「水素の音」みたいなもん 「水素の音」って言いたい人がいるだけ 所有権の複製では意味が通らない事は明らかなので、 誰か >>489 の文をサクッと直してくれていいんだぞ 流れ見てると「所有権の複製」は おかしいから使うな派が半分 おかしいけどまあ好きにすれば派が半分 おかしくない派が約1名 みたいな感じ?
この生産性のない議論に参加している人の割合と考えればさもありなん。
>>560 全く別の所有権なんだからコピーとか言うとわかりにくいだろ。 所有権というか、ポインタなのか、実体なのかって考えれば所有権もすぐに理解できると思うが
>>:567 1つだったものが2つになることを複製と呼ぶのはべつにわかりにくいとは思わんがなぁ
現実のものに例えると適する例がないけど 近い分野だとOSでのプロセスのfork()に近いかな どちらも初期状態は同じでその後は別の道を歩んで値(メモリ)が別の値を取れるようになる これをプロセスの複製と呼ぶから複製という単語自体はわかりやすいと思う
>>570 だから「所有権」に関しては全く別物だろうがよ。。いつまでアホなこと言ってんだか。 >>570 所有権は複製はできないが分割は可能 共有名義の不動産持分みたいなやつ Rustに当てはめるとRcにあたる >>572 >だから「所有権」に関しては全く別物だろうがよ 「全く別物」という理由は?2つになるのは変わるまい。 >>574 人それぞれだと思うが俺は所有権の分割って方がわかりにくいと思うがな。 Rcと紛らわしいから「複製」は使うなというのは納得できるが。 >>571 プロセスはtask_structとかがあってしかも実際に複製されてるからな Rustの所有権はそういうふうに実際にstruct Ownership;みたいのが有るわけじゃない 上の方でも言ってるやつがいるけどborrowcheckerとかのルールや制約を所有権って概念で説明してるだけ だからメモリ上でCopyな(=単純なmemcpyで矛盾が起きない)型が複製されりゃ新しい所有権が作られるし!Copyな(=単純なmemcpyだと矛盾が起きる可能性が有る)型なら所有権もtransferされるってだけ そこに解釈云々だのの余地はなく複製もクソもない >>571 そこで言う複製は値の複製 forkしてもPIDは複製されないのと同じで所有権も複製されない 複製という用語は、2つの実体が作られたよりも、 その2つの同値性が強調される 例えば、一卵性双生児は同一の遺伝子だから複製だけど、 二卵性双生児は複製じゃない。 単に、別々の2つの実体が発生しただけ 二卵性双生児間には同値性が無いから ただ、文書を書いた外人は、copy を同値性という意味で使っていない 長文で説明するのが面倒くさいので、copyという短い単語で、 単に、2つの実体が作られたみたいに、気軽に使っているのだろう だから、copyを翻訳すると複製になってしまう
複製という訳の問題じゃないよね 「所有権のコピー」と言っても「copy ownership」と言っても何も変わらない
PIDとか二卵性双生児とか、根拠や脈絡がない例え話が次々に出てくるのが笑える。 なんでそこまで必死なのかと。
>>571 は曲がりなりにも根拠らしきものを挙げているけど >>578 のPIDはまったくの根拠レスじゃん、 forkで複製されないものを探したらPIDが出てきたとかじゃね? >>570 値はそうかも知れんが所有権は新しくできたものだろ。 新しくできたものを複製ってのは変だろうが。 C++の例外をちゃんと扱うのは難しい、Rustのほうが簡単やろ
>>583 そう感じちゃうのがRustの所有権を理解してない何よりの証拠なんだよなぁ >>587 根拠を挙げて主張しているかそうでないかの違い。国語の問題。 所有権を理解しているかどうかは関係ないんだがお前さんの読解力にも問題があるな。 根拠があるってんなら単にrust公式から定義をひっぱってくればいいのではw それが無いから単なる珍妙なオレオレ用語で終了なんだよこの話は こーいう手合いをいちいち構ってると時間足りないぞ人生は短いぞ
もう誰か本家のissueに「CopyとMoveって何をcopy、moveしてるの?所有権のcopyっておかしい?」って突撃してこいよw
スレ追い切れてないけど所有権だけcopy/moveしてるという主張してる人がいるの?
>>591 聞くまでもなくおかしいだろwww 何をcopy、moveしてるのかはチュートリアル読めよ 参照の説明に合わせてCopy Typeには所有権は無いという捉え方ならかろうじて理解はできる
>>593 おれら匿名の有象無象におかしいって指摘されても聞く耳持たねぇみたいだから本家でぶった切られてこいっていう皮肉なw Cでポインタへのポインタをダブルポインタと言い張ったり Cで関数へポインタで値渡ししてるだけのことを参照渡しと言い張ったり 酷いと思わんかね? 思わん人も居ることのほうが問題
>Cでポインタへのポインタをダブルポインタと言い張ったり >Cで関数へポインタで値渡ししてるだけのことを参照渡しと言い張ったり c++でポインタ渡しを参照渡し言うならそりゃ誤解は出てくるが、そんなに問題にならんわ。 てか extern C で参照渡しは普通にポインタ扱いになるしな。 rustで所有権のコピー言うのは明確に意味がおかしい。
もうええやろこの話題は…こんな枝葉の問題でギャーギャー騒いでたら何も身につかんで… Rust棒を使って気に食わないやつを殴りたいだけなんか…?
所有権 → リソースの解放義務 コピーしたらアカン
c++23以降で契約プログラミングのサポートが入るらしいけど、rustって言語レベルで契約プログラミングサポートしてる?
1週間以上も議論が続いている原因はおそらく copyとmoveの対象の非対称性にあると思う Copy trait非実装の型は「所有権がmoveされる」 Copy trait実装の型は「値がcopyされる」そして「新たな所有権が生じる」
そんな複雑な理屈じゃなくてただのバカが意固地になってるだけだろ。。
>>600 ホントこれ 所有権が何かわかってれば複製なんて考えは絶対出てこない 俺は中立派なので 「Copyトレイト実装型は値と所有権が複製される」 でも構わないし9割以上の人々にはこれで通じるだろう あとはどこまでこだわるかだけだ これ以上その表現は容認できないと続けるならばスレ荒らしと変わらない
>>607 「所有権の複製」は根拠の無いオレオレ用語であり rust公式による定義は一切示されなかった でオシマイの話 議論ですらないし どっち派という派閥の話でもない 一般的に「批判や反対だけならバカでもできる」と言われるので ここは代替案、修正案、改善案を示してはどうだろうか?
匿名掲示板のやりとりで意見を変える人なんていないしとっとと次の話題に移るに限る
複製おじさんが必死過ぎて草www 自分の間違いに気づいてもなお苦し紛れの言い訳テラワロスw
>>597 参照渡しという用語は定義があるからCのポインタ値渡しに使うのは明らかに間違いなわけだが ダブルポインタの方は正式に仕様で規定されているわけではないが俗語として通用しているな。 俗語だから前提無しに誰にでも通じるわけじゃないというところ注意が必要だが。 複製おじさんは自演して複数人を装ういつもの人 いつも間違いを指摘されるが全く反省せず 嘘を書き続けて強弁しつつ自演擁護するのが趣味 C++じいさんと一緒にここに隔離されてる
所有権も複製されるとするとrustの所有権システム破綻するの? そういう話ではなくて用語の用法に違和感があるという議論だよね? 理解合ってる?
>>614 その通り 俺は既に書いたように 『Copy trait実装の型は「値がcopyされる」そして「新たな所有権が生じる」』だが これを『値と所有権が複製される』と表現する人がいても特に困らないしRustの理解で破綻することもない 現実にある所有権に照らし合わせると『所有権の複製』という用法に違和感があるだろうという話 >>613 今日いきなり「複製おじさん」とかいう特異な単語を使う人が立て続けに現れたんですがw >>614 複製されたら破綻するよ 当たり前じゃん 所有権を何だと思ってんの? わかってないの複製おじさんだけだよ >>614 >所有権も複製されるとすると 所有権は複製されない。 一週間バカにされ続けても自ら学ぼうとしないメンタルすごいよな 記事書いたやつも複製おじさんも所有権を変数単位のフラグみたいに認識してるんだよ Copyなら変数の値をコピーして所有権フラグの値もコピーするイメージ(何か問題でも?w) 現実の所有権のように各所有権が個別リソースに紐づくと考えてないから「所有権を複製」しても問題ないと思っちゃう 所有権のメタファー台無し
>>617 >所有権を何だと思ってんの? この質問に答えられるようなら1週間前に終わってる話 自信がなくバカにされるのが怖くて答えられない だから自演して印象操作に走ってしまう それで余計にバカにされちゃう悪循環 Rustスレは 仲介イテレータおじさん 所有権の複製おじさん 算数100点おじさん の提供でお送りします
しょーゆー拳!の複製とは・・・所有してない事では・・・?それとも共有?創造イテレーターおじさん。。。
Rust 1.58で使えるようになったね let var = 123.45; assert_eq!(" 123.450", format!("{var: >10.3}")); let vec = (5..=9).collect::<Vec<i32>>(); assert_eq!("[5, 6, 7, 8, 9]", format!("{vec:?}"));
rust学習してるんだけど コンパイル時検査で並列処理の安全性(可変参照が1つである事)を証明できるんだろうか? 実行時に借用チェックが行われてるという説明があったけどそれは必要なの?
勘違いかも この記事実行時にチェックしてるという意味じゃないかもしれない
>>626 複数スレッドから参照される変数はコンパイル時に保証できないから実行時に保証されるよ Mutexとか Rc<RefCell<T>>のように所有権が共有されてる値を変更する場合なんかは実行時チェックしかできない interior mutability patternと呼ばれるやつはみんなそう
実行時に動的に借用のチェックをさせたいという動機は分かるんだが RefCellっていう名前でそれが表現できてると思えないし 一方で内部可変性を表現してるとも思えないし そもそも内部可変性と実行時借用規則強制と二つのことが一つの名前になってるし じゃあどうすべきだったということも思いつかないけど なんかモヤモヤしてる
Cell<T> は Mutable<T> RefCell<T> は DynamicMutable<T> Cellが何かを一度イメージできるようになると 今のネーミングでも困らないから名前が変わることはないと思う interior mutabilityという名前も最初は分かりにくかった こっちはまだ変わる可能性あると思う
あー、なるほど、名前が分かりづらいのはおれが日本人のせいなのかな、とか思ってたけど、やっぱり慣れてないと分かりづらいネーミングだったか
>>631 中身を書き換えられる『セル』という分かりやすい名前 Cellは内部可変がOK RefCellは内部可変参照がOK 違いが分かりやすいように3種類を持つ構造体を用意 struct S { a: [i32; 3], c: Cell<[i32; 3]>, r: RefCell<[i32; 3]>, } let s = S { a: [1, 2, 3], c: Cell::new([1, 2, 3]), r: RefCell::new([1, 2, 3]), }; // s.a = [4, 5, 6]; // コンパイルエラー (sがmutじゃないため) // s.a[1] = 5; // コンパイルエラー (sがmutじゃないため) s.c.set([4, 5, 6]); // OK 内部可変 { let mut p = s.r.borrow_mut(); // OK 内部可変参照 p[1] = 5; // このブロックを抜けるとborrow自動返却 } assert_eq!([1, 2, 3], s.a); assert_eq!([4, 5, 6], s.c.get()); assert_eq!([1, 5, 3], *s.r.borrow()); >>632 >>636 Cellっていう短い名前にクッキリした役割を描いたのはRust陣営は成功かもね Rust以前に前例があるかどうかは知らんけど >>634 名前の説得力が不足してる疑いはあるよね >>635 それなw >>637 Cell⇔RefCell は 静的⇔動的 の関係ではない Cell⇔RefCell は 直接⇔参照 の関係 そのため余分にRefが付く 静的⇔動的 の関係にあるのは 「&」⇔「RefCell::borrow()」) 「&mut」⇔「RefCell::borrow_mut()」 >>640 それは違う 間違った解釈で無関係な場所に「with statically checked borrow rules」を付けてはいけない 参照借用規則『multi readers xor single writer』に対して 以下が「静的チェック⇔動的チェック」の関係にある 静的チェック: multi「 &」xor single「&mut」 動的チェック: multi「RefCell::borrow()」xor single「RefCell::borrow_mut()」 一方でCellとRefCellの関係は 内部可変性が「直接書き換えOK」⇔「参照書き換えOK」の関係 だからCellに対してRef(参照)が付くRefCellという名前になっている そして参照となるRefCellに対してのみ上述の参照借用規則が適用される >>635 Cell/RefCellと違ってBoxは分かりやすくていい名前だと思うぞ Boxing/Unboxingは他の言語でも普通に使われてるよね? 旧名のOwned<T>や代替案のHeap<T>に比べると確実にいい Boxing/UnboxingはC#やJavaにもあるからBoxには全く違和感なかったけど よく考えるとソースコード上にBoxという文字列が出現するのはRustだけかも?
Scheme でも次の規格に Box が入ることになっている。 (既に導入している処理系も結構ある。)
通常の型TやBox<T>などがSend+Syncである一方 Cell<T>とRefCell<T>は!Send+!Syncであることと引き換えに内部可変性を得ている つまりmutではない参照の内部にあったとしても 「Cell<T>」は「mut T」のように成れて直接書き換え可能 「RefCell<T>」は「&mut T」を得られて参照書き換え可能 ここでCellとRefCellの違いは「&」すなわち「Ref」だからこの命名自体は分かりやすいと思う
>>644 むしろJava等にあるからこそ戸惑ったわ 既にあるBoxing/Unboxingへの理解に対して Rustでは突如構造体としてBoxだもん まあBox と名前を合わせてBoxCellとか逆にBoxをRefにするかした方が統一的な名前づけだったとは思う。
>>648 Box<T>はヒープ上にTの場所を確保してそこを指し示します Cell<T>はヒープ上かどうかは無関係で例えば>>636 の使用例では全てスタック上 したがって「Boxと名前を合わせてBoxCellとか」はおかしいです TとCell<T>はメモリ上の格納場所も実体も同じです つまりCellとは内部可変性という特性を与え示していると考えたほうがわかりやすいでしょう RefCell<T>も同様ですがこちらは参照借用規則の実行時チェックのためのborrowフラグが余分に確保されます もうひとつの「BoxをRefにするかした方が統一的な名前づけだったとは思う」もおかしいです refは参照(reference)の略としてあちこちで使われておりヒープ上かどうかは無関係ですが Box<T>はあくまでもTがヒープ上にあることが主眼です 余談ですがこの関係でRefといえばstd::cell::Refという構造体があり これはRefCell::borrow()が返す型として直接意識することはなく使われています ちなみにRefCell::borrow_mut()が返す型はstd::cell::RefMutです 何も分かってねーなこいつ。 なぜBoxにするか、なぜRefCellにするかってのは要するに構造体のサイズを決定できない場合を想定してるわけよ。 スタックにそういういう動的にサイズが異なる実体をおくことも最近のコンパイラではないこともないが、通常はヒープに置くわ。 文字通りしか考えないで実際の使い方なんか一切考えないやつってのが丸わかりになるって意味では 分かってなさそうなやつにこの辺りについて問いただすってのは割と良いかも。
皆の言ってることがよくわからん そもそもBoxとRefCellって対比させるようなものか?
T -- Box<T> Cell<T> -- RefCell<T> って普通に考えれば対比させると思うけど。
RefCell<T>はTへのrefが作れるCell (実行時にborrow checkする) TとBox<T>はborrowに関しては同じ振る舞いでデータの場所がヒープか否かの違い 全然関係性違うと思うんだけど
「所有権の複製」がおかしいってことは納得言ってくれたん?w
ダメです 明らかにおかしい ポインタ渡しと参照渡しを間違えてるくらいにはおかしい
それは解釈のレイヤの問題。 JIS の情報処理用語の定義では参照呼び (参照渡しという言葉は定義がないが実質的に同じだろう) はオブジェクトの場所を渡すことと定義されていて、 ポインタという形で場所を渡すのも参照呼びの一種ということになる。 言語仕様の話をしているところで異なるレイヤが混ざるのはおかしくはあるが、 実例を示そうとしたり、逆に抽象的に説明しようとしたりすると境界が曖昧になることはある。
じゃああなたはポインタ渡しと参照渡しを間違えてもいいよ
>>658 ポインタ渡しと参照渡しが同じなら、nullptrを渡すのと同じことを参照渡しでどうやるか教えてくれ。 >>657 参照渡しじゃなくてダブルポインタの方だろ。 ポインタ渡しっていう用語も使わないでほしい ポインタ型の時に特別なもんでもなんでもなく 他の場合と同じcall by valueなのだから
rustにおいて参照渡しと言ったらどういう意味になるんだろうか
>>657 そんなレベルじゃないだろw 「値の参照外し」くらいのありえねーやつだぞ >>654 >BoxとRefCellには共通点も類似性も関係性も何もない そうそう、何の共通点も類似性も関係性もないのに The BookではBoxとRefCellを対比させてどういうケースにどっちを使うかわざわざ解説してるんだよなぁ 何の共通点も類似性も関係性も無いにも関わらずw >>666 マジでその二つは関連も類似も何もない 大きく分類してもBoxはスマートポインタの一種だが RefCellはスマートポインタではなく内部可変性という性質の指定のみ clippy様がvoldemort typeを説明せよと仰せなのだが もしかして、名前を言ってはいけないあの人のことをご存じない? >>668 そこはinterior mutabilityの前提としてborrowing rulesを おさらいするためにBoxを出してるだけだから違うだろ。 多義的なオレオレ用語が多くて質は悪いけど the bookに意味もなくBoxとRefCellを登場させてる章なんてなかったと思う。 rust勉強しはじめたばかり、C,C++はだいぶ昔少し経験がある程度 Cとかじゃ、constなpointerは、変数自体がconstと、指ししめす対象自体がconstである場合があり const T * const p; みたいな宣言があったが Rustはデフォルトでイミュータブルなのはわかるけど、mutを付けると、変数自体も指し示す対象もmutableになってしまう? let mut v = vec![1, 2, 3]; v.push(2); v = vec![4,5,6]; どっちか一方を禁止する方法とかあるのですか?
C上がりのヤツって意味のないところにconst付けがち 関数の引数にconst DWORD vとか
>>673 どういうことをしたいのかによるけど let x = Vec![...]; let mut y = &x; みたいにすればyは再代入可能だけどxやvecの中身は変更不可になる 末尾oとかWとかで荒らしてるやつ回線なんだ?亡霊か?
ちなみにRustでのconstとはコンパイル時点で定数(どんな複雑な型でもよい)であること プログラミング言語によってはconstなのに実行時に決まる値(がその後変化しないこと、つまり単なるimmutable)を意味しているので意味の差に注意
>>674 Rustでは関数の引数にconstはないな ジェネリクスとしてのパラメータにはconstがありこちらは便利 >>675 ありがとうございます。 やりたいことは、だいたい同じですけど、少し違って v.push(2)か再代入のv= vec![4,5,6];のどちらかを禁じたいでしたけど あなたの投稿みて考えて let v = &mut vec![1, 2, 3]; v.push(2); //v = vec![4, 5, 6]; エラーになる で一応できました。 でも再代入可能で、中身変更不可にvをする方法は思い浮かびませんでした。 675のようにして、vからyに代入したらyはそうなりますが。 すまんが、Replit使って学習してるんだけどさ たまたま海外勢の動画見たら、同じReplitのはずなのに波線の指摘とかコード補完とか効いててびっくりしたわ VIDEO うちではこんなのならないんだけど・・・・やり方わかる人いたら教えてくれんかな? Repl.itは、リンターやデバッガーからサードパーティのパッケージ、ホスティング、デプロイまで、 すべてを備えた、ブラウザー内の完全な共同クラウド開発環境です
コンパイル時に判明しないゼロ除算やオーバーフロー除算 ex. -128_i8 / -1_i8 はpanicとなるので それが困る場合はchecked_div()を使えばOption型が返りNoneとなる
const fn divide(x:i32, y:i32) ->i32 { x / y } let x = divide(1, 0); //panic const y: i32 = divide(1, 0); //compile error
>>690 定数式の文脈でゼロ除算起きるとコンパイルエラーになるのはある意味当然だと思うんだけど そうじゃないところでエラーになるのが意外だった デバッグビルドでもMIRの最適化とかで定数畳み込みみたいなことやってるのかな >>691 Rustではconst fnな関数を使ってconstな定数を作ることができる つまりコンパイラがその定数を算出するためコンパイル時点で判明する とあるデータフォーマットを扱うライブラリを作っています。 一定形式のレコードが連続で書き込まれて最後には終端レコードが書き込まれるという単純な形式です。 Rust 上でもレコードを追加するのと終端処理のふたつのメソッドがあるだけです。 要は ↓ のように使えるライブラリだということです。 let mut file = File::create("file.hoge").unwrap(); Hoge::new(&mut file).add_entry("string1")?.add_entry("string2")?.finish(); このとき ・ 終端処理は必ずしたい ・ 終端処理はエラーの可能性もあり、それは捕捉したい (ので drop でさせられない) という制約を静的に表現できるでしょうか? 現状では終端処理のメソッドが実行されたときにフラグを立てておいて drop 内でそのフラグを検査するという形にしています。 可能ならコンパイル時に発見できるようにしたいです。
エラーを捕捉したいことをデストラクタに任せない つまりそのような終端処理はdropされる前に終える 例えばBufWriter利用時も終える時は明示的にflushを呼ぶ そしてflushはResultを返しエラーが判明する
>>694 それを制約として表現できるか (終端処理をしていないときにエラーになるように制約を書けるか) という質問をしてる。 つまり出来ないってこと? >>695 それは残念。 言語の機能として用意されてないまわりくどい方法を使うと エラーメッセージがよくわからん (本来の問題とは違う内容が出てくる) ことになりがちだし、 使うときに unsafe や forget をいちいち書いてもらわなきゃならないのは ライブラリとして提供するときにはちょっと汚すぎるなぁ。 エラーメッセージがよくわからん事になりがちだからNGか
>>696 なるほど コンパイラは解析してdropさせるべき位置を把握しているから そこへ至る全ての経路上で例えばFinalize trait実装型はそのメソッドfinalize()を呼んでいないとコンパイルエラーとなる というような制約をするFinalize trait が存在していれば要望を満たす? >>692 非const fnについての話なんだけど amazon primeの記事経由でegui見てみたが結構いいな ネイティブで試してみたが充分実用レベル
GCは標準からは消えたけどライブラリでやればいいってかつてこのスレで言われたことがあるのだが、GCライブラリのデファクトスタンダードってどれだ
>>703 デファクトなんてあるわけないじゃん マークスイープGCするならRust使う意味ないんだから >>704 いや一部だけGC欲しい時は普通にあるからそれは言い過ぎ >>706 これはいい記事だな 次からテンプレ入り希望 >>702 eguiは毎回60fpsで全画面を描き直すことで差分描き直しを避けて簡単にしてるようだけど 省力化したい方針と合わないや ゲーム向き? >>705 GC使ってもRustのRAIIを無くせるわけじゃないから そういう時はVecに入れて使って あとは任意のタイミングで未使用の要素の区画を再利用という感じにしてる >>706 Rustは各種bookを読んであとはstd docとreference見ながら コンパイルエラーの通り直すだけで何とかなるから書かれている通りだね 付け加えると基本要素については覚えていないメソッドのために回り道しがちなので Option Result slice str Iteratorあたりの全メソッドは一通り認識しておくといいかな >>706 >Mistake 3 : Start by implementing a graph based algorithm その通りだとは思うけどRustと相性の良いアルゴリズム集的なのは欲しいとも思う >>711 汎用データ構造の大部分を使うな、という話になりそうだからなぁ。 スタックとキューくらいは楽に使えるのかね。 dynや lifetime heavyは失敗しながら学ぶのでいいと思う 普通の人はしばらくやってれば気づくから
deny(rust_2018_idioms)はガチのマジでオススメです
データ構造が大事なんだって言われながら育ってきた身としちゃあ辛い話じゃねえか 自分で作らなけりゃいいんだな!ってslab_tree使って 「ある条件に合致した子ノードから、ルートまでの値をリストで取り出す」 みたいな処理を書こうとしたら怒られるんだよな。 node_ref.parent() : &Self<T> -> Option<NodeRef<T> っていう型なもんだから、次々に親をたどるために while Some(parent) = node.parent() { node = parent; ...} みたいな処理書くと、当然怒られる。 何とか抜け道無いかって探したら、node自身じゃなくてそのIDを使えばよかったんだけど、 いつも抜け道があるとは思えない。辛い。
Rustで普通にやってるとスレッドセーフを強いられるから制約がキツくなるんだよね
大昔からの単純なポインタ相互参照だと ダングリングポインタ・多重解放・解放忘れなどが全く存在しないことを検証すべき範囲が一般的には広くなりすぎる もし狭い範囲に閉じ込められるケースならば閉じ込めた中でunsafeを用いたとしても効率的な型を提供すればよい 標準ライブラリにあるヒープを用いる型は全てそのようにして作られている
>>684 今それ読んでる 出版物だけあって日本語の質はずっといい >>718 もしかしてThe Bookの勝手訳の質と比べてる? >>720 勝手訳というのがなにを指すのか不明だけどオリジナルのThe Bookからリンクされている日本語訳のことであればそれと比べている そんなのを見るのは極初期だけで些細な話 その後はdoc.rust-lang.orgとdocs.rsしか見ないのだから
あれは害悪レベルの訳だから初期でも見ない方がいいよ ここでよくおかしなレスしてる人もあれの影響なんじゃないか?
どうせ>>722 のドキュメント見ないと先へ進めないしほとんどは中学生でもわかる平易な英語 日本語訳の質にこだわるような低レベルのやつはほっとけばいい Mistake 2 : Dive in without looking at the book
>>726 まさにそれで最初は日本語訳でもいいがその後にthe bookを直接見るべき そしてどの英単語でどう表現されているかを掴めば日本語訳がどうかに関わらず先へ進める 一貫してるぞ 日本語訳の質にこだわるような低レベルのやつはほっとけばいい これしか主張していない
じゃあ和訳の質を話題にしている低レベルの人同士の会話はほっとけばいいのにw
日本語イラネ言っている人は技術資料が英語でも 日本語でも生産性が変わらない人なんだよね? アメリカの仕事でもした方が稼げるんじゃない?
和訳の質にこだわってる人たちも唯一役に立つチャンスがあるよ それは和訳の改善案を提案して質の向上に貢献すること しかし和訳の質にこだわってるここの人たちは批判だけで提案がないから残念な人たち
和訳の質にこだわっている人たちの質にこだわっている人は何の役にたつの?
どこの食堂が美味いか話しているところに割り込んで、 不味い方を立て直してこいと言うくらいナンセンス。
>>725 その結果、所有権を複製しちゃったんでしょw とはいえ元の話は 無料の炊き出しがレストランより不味い みたいな話なのでそりゃそうだろうとしか 誰もがオライリーを気軽に買えるわけでもないし それぞれ意味はあるだろう
ざんねんながら悪文どころか誤訳だらけで無料の炊き出しよりひどいのがままあるのが技術書の世界
>>738 誤訳だと言うなら改善案を提案して質の向上に貢献するのがいいよ それができないなら単なるイチャモン付けるだけの残念な人 クソ不味い飯屋にわざわざ改善案を提案するやつww 「批判するなら対案出せ」と同じアホ理論www
両立するよ。 改善に取り組みつつ現時点では良くないところもある (信頼しすぎるな) と初心者に警告するのは何も矛盾しない。
>>739 ではイチャモン付けるだけの残念じゃない君が俺の代わりに改善案を提案しておいてくれ >>743 自分は現状の和訳で問題ない派 そんな些細なことよりも和訳だけでなく原文も併用した方がよく その後は原文だけの世界なのだから 日本語訳の質にこだわるような低レベルのやつはその先へ行けないため日本語訳にこだわる
和訳にイチャモン付けるだけの残念な人は「複製おじさん」を連呼する人でしたか
>>543 からの流れを見ると確かに複製おじさんは英語読めないっぽいが Copyを「所有権の複製」と思い込むのは日本語訳の質が原因ではない気がする そんな超初心者入門のところでもめてるのかね trait Copyを実装している型は複製されて、実装していなかったら移動となるだけだぞ 所有権は難しくない
>>489 の文章が間違ってる、いやおかしくない、って揉めてただけだよ まあ気付けば普通はおかしいと思うんだけど >>751 所有権 Copy実装型は常に所有権が分岐する >>753 複製(copy)と分岐(branching)は全く違う意味だけどCopy実装型だと所有権が複製されつつ常に分岐する?? どういう意味? >>754 初期状態は全く同じ つまり複製されて分岐する それ以降は異なりうる Rustの所有権というのは権利というよりも所有しているリソースの解放義務を指している 複製できたら所有権管理の意味がない 分岐はもっと意味不明
>>756 複製され分岐するため リソース解放義務はそれぞれに生じる >>759 複製おじさん、嘘ついちゃダメだよ 詳しくはdoc.rust-lang.orgとdocs.rs見てねw 複製おじさん連呼する人は、もちっと具体的に指摘してくれるとありがたいんだが。
嘘ではない もちろんCopy実装型の時点でヒープは使われないので解放といってもスタッフ上のみだから実質何も行われない そのためデストラクタも容認されていない その観点からCopy実装型は所有権がないと主張する人もいるくらいだ
>>761 おじさんを連呼してる人はスレを荒らしているだけのクズ 説明や代案や根拠などを語れない >>762 何も行われないのに複製されて分岐してそれ以降は異なりうるってどういうことだよ 矛盾だらけ >>761 ,763 複製おじさん得意の自演乙www 嘘つきは分岐の始まり > そんな超初心者入門のところでもめてるのかね > 所有権は難しくない 【超初心者向け所有権の説明】 Copy実装型は所有権が複製されて分岐してそれ以降は異なりうる 難しくないw
>>764 この件は有名な話でRustコンパイラのサボり。 Copy型は複製分岐されて各々がdropされるのが正しいけど、スタック変数のみだからそれをサボっている。 つまり現在のコンパイラ実装は正しくなくて、dropしちゃうとサボりのせいでメモリunsafetyを引き起こす可能性がある。 だからCopy型はDropを現状では許していないという話。 rustc --explain E0184 を見てね。 >>767 なんか仕様と実装を混ぜて話してるけどなんで? 普通、仕様にそって実装がされるはずだけど、(この部分に関しては)実装に仕様が引きずられてるってこと? それとも仕様通りではないってこと? 将来実現されるであろうあるべき仕様が実装の都合で実現できていないから、それと矛盾しない範囲に仕様の範囲を制限した、という理解で良い?
>Copy型は複製分岐されて各々がdropされるのが正しいけど、スタック変数のみだからそれをサボっている。 まーた勝手な思い込みの妄想で嘘垂れ流してる いい加減にしろ
>>767 それ1.0以前の話で今とは実装も前提も全く違うからエラーメッセージ修正したほうがいいやつ もし仮にDropかつCopyな型が実装できるようになったとしても所有権は複製されないから >>773 >理論的にはCopy型は複製分岐されて各々がdropされる形が正しくて 複製分岐されるのが正しいという根拠は? >>774 Copyなので複製分岐されるのは当たり前。 今はそこが論点ではなくて、複製分岐の後に各々に解放処理が生じるけど、現在は正しく実装されていないので仕様に制限があるとコンパイラのメッセージでも出る話。 >>774 複製分岐おじさんは日本語通じないからまともに相手しても時間の無駄だよ いつも傍から見ていてパターンがわかってきた 普通は「○○○は間違っている、×××が正しい」となるんだけど なぜか「○○○は間違っている」だけで終わって代わりとなる対案が出てこない >>771 のように「勝手な思い込みの妄想で嘘」と否定するだけだったり >>774 のように「正しいという根拠は?」とこれも同じパターン 全てに共通するのは対案を出さずに否定ばかりしている言動 >>775 今は複製分岐されるのが本当正しいと言えるのかどうかが論点です 論点をずらさずにそう考えた根拠を示して下さい ありませんか? >>779 Copyを実装している型は使われると複製され分岐すると自分も当たり前に思っているけど 異なる意見を持っているの? 否定ばかりしていて対案を出せない人はどこの世界でもダメな人扱いになっちゃうから対案を出すのがお勧め >>781 まじかよ反応して損した この流れ何の意味もねえな Copyを実装する型の変数にはそもそも所有権が存在しないということかな。
ソケット、ファイルハンドラあたりがコピーされたらそら問題だろうからな。 >Copyを実装する型の変数にはそもそも所有権が存在しないということかな。 copyされたら所有権がないとかめちゃくちゃだな。int値だって共有されるかどうかはかなり問題だっての。
>>782 値が複製されることに異論は無いんだけど、彼は>>753 などで所有権が複製されると言っていて、そこがおかしい。 たとえ話って逆に理解を妨げることも多いよね(´・ω・`)
複製おじさんにも唯一役に立つチャンスがあるよ それはE0184の改善案を提案してRustの質向上に貢献することw しかし複製おじさんは妄想だけで改善が見られないから害でしかない
複製おじさん大人気だな 個人的には「所有権が分岐する」のほうが衝撃的だった
Rustの学習してるのですが、モジュールがどう使われるものなのかいまいち想像がつきません 他の言語でいう、スタティッククラスのような使い方になるのですか? クレート内部にはモジュールを通じてアクセスしてもらうような、アクセサーのような感覚でいいのでしょうか?
なんで所有権が複製分岐なんていう言葉を発明しちゃうんだろな 実際に所有権をツリー構造みたいなものでイメージしてるのかな
>>791 モジュールはコードのかたまりでnamespaceを構成するもの Javaならパッケージ、C#ならnamespace 複製は値と所有権のペアがまるごと複製されるというイメージでわからなくもないが分岐は本当によくわからない
>>792 何事も正しく理解できないクセがついてて 同時に、そんな自分を客観的に理解できてないから 独自用語乱発になんの違和感もないんやろな 複製おじさん自演認定連呼の人も大概だけどな 技術について語るスレなんだから個性を出すな与えるな
変数毎のメタデータに所有権管理のためのフラグか状態変数があってその値も複製されるイメージだったんでしょ それを匂わせることを確か書いてた気がする 仮にそういう実装だったとしてもそのメタデータ自体は所有権じゃないんだけどね 複オジは反省して分岐して欲しい
>>784 Copyを実装する型の変数にも所有権は存在するぜ 使われるたびに複製されて別々の所有権になる 例えば借用ルールなどもそれぞれ個別に適用されるようになる 複おじはまずコテハンかトリップつけてほしい 名無しに紛れ込んでスレ荒らすのやめてほしい
>>799 で合ってると思う私も何か勘違いしてる? もし違うならば正しい情報を知りたい >>803 指摘の中身が無いから何をどうすればいいか傍から見てさっぱりわからんのだが。 >>804 それそれ おじとかオジとか言ってる人の書き込みを遡って見ても中身がないかコピペばかり おじとオジをNGにすればよいのかな >>802 正しいよ。 >>799 で正しい。 値が複製されると同時にそれぞれが所有権を持つというのは根本的な原理そのもの。 「所有権が複製される」のではなくて、「値が複製されるとき、複製された値には新しい所有権が生まれる」と表現すべき、ってこと?
>>806 ありがとう おじオジ連投の人はNGにします 単なる揚げ足取りでしょ 本人もよくわかってないと思われ
あちこちでrustおじさんが暴れてるんだけどこのスレでも暴れてんのな
>>808 その2つの解釈に何の違いがあるのってことだよな 論理性がない、客観性もない、実績もないのに上から目線の奴いるよね 素人相手にマウント取りたいアフィブロガーやアフィチューバーの類なんだろうけど
>>812 「言葉足らず」だっだという事にしたいのねw 所有権を実装の観点からだけ見た場合はCopy型に所有権は(設定されて)ないと考えるのは妥当 説明用の概念として見た場合はshared referenceを除くCopy実装型にも所有権があってコピーされた際にその値に対する新たな所有権が発生すると考える方が妥当 公式は基本的に後者
>>808 「所有権が複製される」という言い方は 単なる表現の問題として矮小化されるものではなく所有権という概念を根本的に誤って解釈してるのが大きな問題 それを吹聴するのはRust入門者の学びを妨害する行為なので叩かれてる >>818 所有権という概念はどう解釈すべきなの? TRPLの説明では take ownership という表現も登場するし辞書的な意味で ownership が使われているのでルールのことを指すだけではないようだ
Each value in Rust has a variable that’s called its owner. ... The ownership of a variable follows the same pattern every time: assigning a value to another variable moves it. When a variable that includes data on the heap goes out of scope, the value will be cleaned up by drop unless ownership of the data has been moved to another variable. このあたりの記述を読む限り、すべての値は owner となる variabke を持つ (variable が値の ownership を有する) と表現して良いように思う 対象はすべての値なので、 Copy を実装した値にも当然 owner と ownership が存在する なので、 let a = 1; let b = a; という式があった場合、aは1という値のownershipを持つし、bはaの値のコピー(1という値のコピー) のownershipを持つことになる a の持つ ownership と b の持つ ownership の関係性をどう表現すべきか、というのが議論の対象という理解で良いかな?
aという変数が持つ1という値とそのownershipがまとめて複製されてbに渡されると思えば、owenershipの複製という言葉も不自然ではないように思う 複製といいつつ own する値や owner となる変数が異なることに違和感を持つ人がいるのもわからんでもないが、 この構造は Box<T> の clone と同じなので、 clone を複製と呼ぶのであれば、 ownership も複製されるものとして間違いではないと思う 初学者向けの説明として適当かという議論は別途必要だとは思うけども
それは単に値のコピーですやん 値が複製されてるだけですやん bは複製された値のオーナーになってるだけですやん aの所有権はどこにも複製されてないですやん
>>824 > 値とそのownershipがまとめて複製されてbに渡されると思えば その仮定が偽。 >>824 Box<T>のcloneと同じなら どうしてownershipも複製されることになるの? 2つの繋がりがよく分からない >>827 なるほど こういうの論点先取と呼ぶのか 勉強になった オーナーシップの説明にオーナーシップを使っているのか なるほど論外だ
所有権をCopyするって書くのをやめて、値をCopyする、にしてくれればそれで済むのに、なぜ所有権の複製を正当化しようとするのか
べつに値をコピせずに所有権だけコピーするとか言っているわけでもなし、そんなに問題かね?
>>824 だからー、値が複製されて所有権が新規にできるんだってば let a = 1; は値1の所有権をaが持つということ ここでその値1の所有権を複製してしまうと、同じ値の所有権を持つ人が複数存在してしまうことになる let b = a; で実際に起きるのは、値1のコピーとして値1'を生成し、その所有権をbに割り当てている 値1と値1'は数値としては同じに見えるとしても違う値であって それぞれ別々に所有権がある 1'の所有権が1の所有権の複製である、という理解は 権利書をコピーして記名欄だけ書き換えるみたいな イメージなのかもね 実際にはその記名欄が所有権の全てであって、 複製する部分というのは存在しない
Copy実装型にも所有権はあって借用ルールに従う そして使われる時に値と所有権は複製されて別々の値と所有権になる そのため借用中だと複製できない let mut a = 123; let a1 = &mut a; let b = a; *a1 = 345; println!("a={a}"); println!("b={b}"); 例えばこのコードは借用中のため複製できずコンパイルエラー
複製するという言い方は、複数の権利者が (一時的にでも)存在し得るという理解につながるので 単に言い方の問題ではなく避けたほうがいいと思う
どうでもいいならそれでいいんだけどね 中身の無いどうでもいい話をしましたってことで
>>836 そういうことか! 値だけなら複製できてもよいのに 値と所有権が複製されるために借用中だと複製できないわけか その通り だから「(所有権と関係なく)値が複製されて、その値に新たな所有権が生じる」よりも 「値と所有権が複製されて、別々の値と所有権になる」の方がRustを正しく理解できる
wwww 複製オジのエクストリーム解釈で自演してもモロバレやでwww
>>835 現実世界から借用した用語は多いけど、意味まで完全に同じとは限らないからその議論は無意味だね。 そもそも現実世界の「複製」は「同じものを新しく作ること」だしw OwnershipのルールとReferenceのルールが整理できてないんだね 分かった気にならずに一からやり直したほうがよさげ
>>836 めちゃくちゃな解説でびっくり!! 複製おじさんはこのレベルだったか >>844 僕は>>836 の説明で納得した そして実際にコンパイラエラーも確認した 君がそれを違うと言うならば どの部分がどういう理由でどのように違うのかを皆に示さなければならないと思う >>836 それはmut借用中の値は使用禁止(コピーのために値を読み取るのも当然禁止)ってRustの基本ルールに違反してるってだけで 所有権の複製なんて新たな概念を持ち出す必要はないと思うが 実際エラーメッセージもそう書いてあるし >>846 借用中の値が使用禁止は所有権が伴っているからこそ生じるルール だから「(所有権と関係なく)値が複製されて、その値に新たな所有権が生じる」よりも 「値と所有権が複製されて、別々の値と所有権になる」の方がRustを正しく理解できる 複製派の人って複製元の所有権と複製先の所有権の 共通部分はなんだと考えているの? 流石に共通部分が全くなければ複製とは言わないよね?
>>848 同じ型で複製の直後の値が同じものに対する所有権 全く異なるものに対する所有権が新たに生じるわけではない 所有権も複製されている ちなみに現実のものに例える話はナンセンス なぜなら現実世界では土地の所有権だけでなく土地自体も複製できないのだから じゃあ「所有権」って言葉を使うのもやめることを提案してみたら?
>>849 土地は複製できないけどそれの何が問題なの? 値として見るならCloneじゃない型と同じじゃないの? >>848 これ知りたいね 単に引っ込みがつかなくなってるのではなく 真剣に複製されると思っているのであれば 何らか共通部分があるんだろうから >>849 そうすると所有権という情報には、型と値が含まれてるってこと? その場合、値の変更は所有権の変更を伴うと考えている? また、型と値が同じものはたくさんありうるけど、それらの所有権を区別するものはなに? リアルな世界では土地もその所有権も複製できないけど こちらの世界では値も所有権も複製できる と考えるだけで矛盾なくRustを理解できると思います そこに矛盾はありません
こまけえこたあ良いんだよ!! コンパイラ黙らせた奴の勝ち!!
>>852 それぞれ固有の値を持たないのであれば全部同じ「所有権」では? >>847 >「値と所有権が複製されて、別々の値と所有権になる」 別々のものになってて複製? 言ってて変だと思わんの? そもそも発端は入門者向けドキュメント >>486 にて、「所有権の複製(コピー)」とかいう言葉が出てきてこれではダメだ、っていうのが発端だからね 入門者向けドキュメントなんだから、正しい言葉で、正しく伝わる表現をしてほしいのよ 結局のところ元の記事にあったように 「所有権とは、文字通り変数が値を所有できる権利のことです。」と間違って捉えてるってことだろうな
>>858 それは正しいだろ 少なくともその解釈でRustの仕様と矛盾する点は何もない 土地は複製できないって主張もよくわからなくて 例えばコピー機は別に原子レベルで複製しているわけでもなく 人間が見て同じに見える程度に見た目を再現してるだけなわけで 同様に土地だって同じ形状に造成できるわけじゃん だから複製というときにはオリジナルのどこを再現したかが重要で 何が共通部分で何が差異なのかをはっきりしてほしい
>>857 複製ってのがそもそも別のものを作ることなんだが リソースの所有者はリソースを解放する責務がある 主としては値が変数に束縛されることで所有の関係が作られる このへんをいろいろひっくるめて所有権の概念になるわけで、こういった概念である「所有権」そのものを複製したり作成するというのは、やはり言葉としておかしい 束縛関係を複製とか言われても意味わからん
ここで"もの"かそうでないかを区別する意味ってある?その場合の"もの"ってなに?
所有権とは所有にまつわるルールのことというのはTRPLに書いてある通りだと思うんだが take ownership など、所有権という物をやりとりしているように読める文言はどう解釈すれば良いんだ?
所有権の話はもう禁止してくれ、唾飛ばしながら「ワイが一番所有権分かってるぞ!」とかほんまどうでもいいわ コンピューターサイエンス学科出でもないのに、もう駄コードを書く仕事に戻れ
>>868 が言ってる通り公式ドキュメントと矛盾がないように書くべきでしょ そうすると、copyするのはvalueであって、ownershipはcopyしない ownershipはtakeしたりtransferするもの >>866 の最初に書かれてることは良いけど、そのあとは意味不明 この自演おじさん、そこらじゅうで同じ芸風で荒らし回ってるから本当にタチ悪い。
結論: 「所有権の複製」は根拠の無いオレオレ用語であり rust公式による定義は今回も一切示されなかった でオシマイの話
>>868 take ownershipのownershipは”もの”じゃないよ もうちょっと英語勉強したほうがいいんでは? >>875 理由を言わず間違ってるとだけ指摘して勉強した方が良いとマウントとってくるいつもの人だ 反論できないから空っぽの指摘しかできないのかな unixのファイルシステムの権限周りの継承とかその辺とごっちゃになってんのかね? どうして所有権をコピーみたいな話が出てきたのかわりと謎
謎だよな C++から来てるわけでもないし どこから来た発想なんだろう?
>>876 説明してもらっても聞く耳持たないから もう理由は教えないみたいなことを言われてなかったか? >>879 その説明へのレス番号貼るだけでもいいよ 所有権ルールと参照ルールを混同してたり 所有権が複製される構造はBox<T>のcloneと同じと言ってるところに 勘違いのヒントがありそうだか皆目検討がつかない 誰か解読してくれ
ここまで見てる限りどっちでもOKな話だな 値と所有権が「!Copy型は移動」「Copy型は複製」との説明でもRustの理解に支障がないのも事実 一方で現世界にない「所有権の複製」という表現に違和感を持つ人が存在することも理解できる ただし後者はあくまでも心の内なる話だから前者を崩せない限り不利っぽい
>>886 いや、>>870 で正しいの書いてくれてるのに、公式でもそうなってるのに何故に頑なに所有権の複製を広めようとしてるのよ笑食べて Rustの公式ドキュメントを調べると "copy of the value" という表現はたくさん出てくるが、 "copy of the ownership" のような表現は見つけられない おそらく公式ドキュメントでも "copy of the ownership" みたいな言葉が使われそうになったときは意図的に排除されてるんだろう もし "copy of the ownership" みたいに変更しても問題ないと思うなら、公式ドキュメントのリポジトリでそういうふうに提案してみてくれよ Contributionのガイドを参考にコミュニティに書いたり、GithubでPull Requestするだけだからさ https://rustc-dev-guide.rust-lang.org/contributing.html ownership の take や transfer という言葉が出てくるのは !Copy な値についての説明で、 Copy な値については ownership 絡めて説明されてないよね ownership rule 自体は全ての値に適用されるから本来は Copy な値の ownership についてとうルールが適用されるかという説明はあった方が良いかもね 元々の初学者向け記事ではTRPL英語版にない部分の説明をするにあたって所有権の複製という用語を発明したわけだけど どう説明するとわかりやすいんだろうか
>>886 >Rustの理解に支障がないのも事実 めちゃくちゃ支障が出てますやんw 所有権が複製されると思ってるからRustの基本ルールが理解できない >>836 所有権が複製されると思ってるからThe Bookの意味が取れない >>868 >>888 Rust的には「変数(値)の所有権がある」という表現が既におかしいぞ >>890 > 元々の初学者向け記事では(中略)所有権の複製という用語を発明したわけだけど オレオレ用語を初学者に平気で刷り込んで平気ならば > どう説明するとわかりやすいんだろうか 今後一切あらゆる場所で説明などしないでほしい 個人が複製を分かりやすいと思うのは自由だけど 初心者に広めるのはダメだと思うがな もっと明らかに初心者向けの例え話とわかるような用語ならともかく いかにも公式の技術用語っぽい見た目をしてるわけで これを知った初心者がもっと深く知りたいと思ったときに ググっても全く情報は出てこないし、誰かに質問しても「なにそれ?」ってなる 少なくとも公式の説明に沿った言い方なら、それで理解してる人が 大勢いるから、そういった問題は生じない
自分の理解不足を何で公式の落ち度みたいにすり替えてるんだ。間違いを認めたら死んじゃう病なの?
>>881 Box<T>が出てくるあたり所有権を値へのポインタ的なものとして考えてるのかもな まあそれでも複製はされないからイミフには変わりないんだが 勘違い勘違い言うけど>>808 以上の話じゃないように思うんだが。 自分も>>808 で良いと思うけど公式の説明と表現が同じになってるかは気になる >>808 >>「値が複製されるとき、複製された値には新しい所有権が生まれる」と表現すべき だからそれが間違っている 値には所有権は無い 入れ物に対して所有権がある 例えば&mutはその入れ物に対する書き換え可能な参照つまり所有権の借用 >>808 を肯定する連中はRustをわかっていない 複製オジが遂に撤退戦をはじめたかw なんで所有権が複製可能だと思い込んだのか説明してくれれば 他の人の役に立つのにな
>>900 「入れ物に対して所有権がある」も微妙な表現で 「入れ物となる変数が複製された値の所有権を持つ」の方が適当だと思うけど、どう思う? 流れぶった切ってすまんけど質問 「借用」と「参照」の違いってなんなん?
>>902 値は書き換わる物 だから値に所有権はない 入れ物に対して所有権がある 解放する対象も入れ物であってその値ではない >>904 c言語のfreeって明らかに値を解放してるように思えるんだが freeした値はその後使えないがその値を入れていた変数はその後も使える ownerに対する所有権があるような話になってよくわからんね。
>>903 参照は変数の種類で、借用は参照の使い方とか参照同士の関係とか状態のこと。 明確に書かれていないけど、そのへんを意識してThe Bookのreferences and borrowingあたりを見ると良いよ。 >>905 C言語のfreeでも入れ物を解放している 入れ物の中にある値を解放しているわけではない そしてmalloc/freeで対象となる変数は入れ物を指している つまりその変数自体は一つ階層が異なりポインタである そのポインタ変数を書き換えても別の入れ物を指すようになるだけ 入れ物の中身が書き換わるわけではない >>908 いいえfreeは入れ物にある値を解放しています 入れ物を解放しているわけではありません そもそもfreeに限らずc言語の関数はすべて値渡しなのでfree(入れ物)と書いたらfreeには入れ物にある値が複製されたのが引数として渡されて入れ物に関する情報は一切渡されません c言語の関数が操作できるのはこの複製された値です もし入れ物を関数funcで操作したい場合はfunc(&入れ物)と書きます この場合も&入れ物という値が操作されます 繰り返しになりますがc言語はすべて値渡しなので決して関数に入れ物を渡して解放するなどと言った操作をすることはできません 解放されるのは値です 入れ物の参照を関数に渡すには&入れ物という表記を使いますが&入れ物も値です これは参照呼びと言われますがただの値渡しです あなたは明らかにプログラミング初心者なのでこのレスが理解できるようになるまでこのスレには書き込まないでください >>909 それは君が抽象的なセマンティクスとポインタ等を介するコードの区別が出来ていない初心者だから理解できないのだろう Rustではこの違いが特に大きいのでその区別を付けることが非常に重要 >>900 これは同意 何が何を所有してるのかという主語目的語を意識せずに フワッと分かったつもりになってるからなんだろうね 俺は初心者なのでフワッとすら分かっておらず、このスレでは一体何が議論になっているのかよく分からない。
>>910 別に抽象的なセマンティクスでもないよ 君が言っている「入れ物」でさえ操作的意味論的にはアドレスを表す単なる「値」として表現されていることを知るべきだね ちゃんと理論的にはどのように定式化されているのかを知らないで入れ物だとか言った自分の無知を埋め合わすために勝手に導入したオレオレキーワード使って他の人を困らせてる辺り一向に君は初心者から脱却できないと思うよ 「入れ物」(笑)なんかじゃなくて実際に使われているテクニカルタームを使うことから始めれば? 知らないの? 学術的に使われた用語だからテクニカルタームではないな
freeに渡すアドレス値を「入れ物」と呼ぶか「値」と呼ぶかでもめているように見える
>>914 その理解はおかしいよ 例えば struct S(i32); struct SS(i32, i32); let i = 100; let s = S(200); let ss = SS(300, 400); let a = [500, 600, 700]; この時にあなたの理解だと各変数に入っている「値」はアドレスなの? もちろん生成コードにおいてスタック上のアドレスが用いられるのは事実だけど Rustというプログラミング言語のレベルではそのアドレスは出てこずに抽象的に捉えるべき話でしょう >>918 それらの変数にはすべてそれぞれの実体が入っています アドレスではありません 全ての「アドレス」は「値」ですがだからといって全ての「値」も「アドレス」であるとは言っていません まずは読解力を身に着けましょう もっと正しく理解をしましょう >>919 では、あなたの主張するアドレスはどこに出てくるの? let a = [1,2,3]; let v = vec![1,2,3]; どちらもアドレスではないですよね >>920 失礼しました 配列は先頭要素のアドレスが変数に格納されるでしょう これだけで済む話です 抽象化なぞそもそも必要とされる余地はありません 所有権を持つのは値じゃなく変数 これはいいよね オライリー本とかは少し違うけど 少なくとも公式は値が別の値を所有するとか 値が別の値の所有権を持つという考え方は採用していない で解放のほうだけど 解放する対象はメモリ領域であって値でも変数でも無いよね 便宜的に「変数(の指してるメモリ領域)を解放する」とか 「値(が格納されてるメモリ領域)を解放する」という言い方をすることがあるだけ
>>921 それも違う 配列の先頭要素のアドレスが変数に格納されているわけではない Rustではもっと抽象的なセマンティクスでプログラミングをするし配列の長さも持つ 値に対する所有権を変数が持つってことなら>>808 は特におかしいとは思わないが? 逆に「入れ物」(変数?)に対する所有権とか言っている>>900 の方が理解しにくい。 >>922 私は操作的意味論のモデルに乗っかって表現したまでです 操作的意味論ではメモリ領域を示すアドレスは値として表現されています ある特殊な操作的意味論で定義された理論をベースにしているRustでメモリ領域のことを値だと表現するのは間違っていないでしょう 逆に入れ物や変数だといった表現をこの文脈で使うのは言語道断かと思われます >>921 それは違いますよ そこでアドレスという考え方はしませんし、実装で見ても間違っています 例えばあなたの考えでは以下の4つの変数のうちアドレスとなるのはどれですか? struct S(i32); struct SSS(i32, i32, i32); let i = 100; let s = S(200); let sss = SSS(300, 400, 500); let a = [600, 700, 800]; >>923 すいません さらに配列の長さも保持しているのならなおさら抽象化のレベルは下がりますよね? 自分が何を言ってるのかわかっておいでですか? もしかしてRustの抽象化レベルってCよりも下なんじゃないんですか?(笑) なんでこのスレでは操作的意味論とC言語とRustをちゃんぽんして語ってるの? みんな器用だね?
>>926 変数という用語も正しくはありません まず第一にRustでは束縛と呼ばれます 正しく理解してください それができないならばあなたはこれ以上スレに書き込まないでください ちなみに私がアドレスだと言っているものはあなたが変数だと言っている物です そもそもアドレスという表現も不適切なものですが悪しからず >>929 ついに馬脚を現したな Rustでも変数(variable)と呼ぶことすら知らないのか もちろん束縛(binding)も狭義の代入(assignment)と区別するために用いるが そこでも束縛や代入の対象は変数である >>929 変数という用語で合っていますよ 早く>>926 の質問に答えてください Rustでアドレスという考え方をするあなたが間違っていると明白になりますから >>930 Rustの用語では束縛の対象は名前です 変数ではありません Rustが便宜的に変数と使っているのは説明のためにユーザーにRustなりに歩み寄っているからです あなたが「入れ物」だといったよくわからないキーワードを導入したのと基本的には理由は同じです 操作的意味論では束縛の対象は変数ですが代入の対象は変数ではありません メモリ上のある位置です 便宜的に言えばアドレスです 再三の忠告になりますが正しい理解が出来ないのであればスレに書き込まないようおすすめします >>931 i,s,sss,aがアドレスです まずは>>929 を理解する読解力を身に着けてください > Rustでアドレスという考え方をするあなたが間違っていると明白になりますから 逆にアドレスという考え方をしないのですか?(笑) 手続き型言語の重要な機能ですよ? ocamlなどと言った非純粋な関数型言語にすらありますが(笑) アドレスという考え方を他の言語利用者が使うのを許せないのであればこの機能がないHaskellなどをご利用してくださいとしか・・・(笑)(笑)(笑) Rustには触れないでくださいね😂 >>933 残念ながら>>926 の変数i,s,sss,aに入っているのはアドレスではありません あなたが完全に間違っています >>934 変数i,s,sss,aにアドレスが入っているなどと言ってません 読解力も理解力もないんですね i,s,sss,aは変数ではなくてアドレスという名の値だって言うことを理解できないとあなたはいつまで立っても初心者のままですよ??(笑) >>934 間違っているのはあなたです これで明白になりました 間違いを認めたくないおじさんが延々と言い訳するスレ
ID:6AYXkq/G氏は >>919 で「それらの変数にはすべてそれぞれの実体が入っています アドレスではありません」 >>921 で「失礼しました 配列は先頭要素のアドレスが変数に格納されるでしょう」 これで全く理解できていないことが露呈してからさらに暴走中ですね >>935 Rustにおいてそれらの変数i,s,sss,aはアドレスではありません 抽象的な考えが苦手ですか? >>939 Rustを含めアドレスという言語機能を持っている手続き型言語で変数と呼ばれているものはただのアドレスです Rustでいう値が束縛された名前は操作的意味論における変数に対応していてあなた方がいう変数とは操作的意味論におけるアドレスを表現するものの対応物です 本来変数とアドレスは同義のものでc言語の規格で完全にポインタとアドレスが同じものとして扱われ区別されないのと同様に区別する必要性がないものです 現にポインタもアドレスも変数も操作的意味論では区別されていません このあたりを理解できない限りID:6Qn4bKwUは永遠に初心者のままのようだ🤣 cのfreeは値「で」解放してるだけなんだけどなw int *p = malloc(sizeof(int)); // 仮に p = 0x5617ae143260とする free((int *)0x5617ae143260); // 値でfreeできる 値をfreeしてるわけでもなく 入れ物を解放してるわけでもなく 値をfreeに与えてやってあとはむこうでうまくヒープを解放してくれる ヒープ解放のきっかけを値で指定してるだけ
>>941 横レスだけど 仮にこの世にコンパイラも実行するマシンもなくて、Rustのコードだけが紙に書かれてたとして それでもi,s,sss,aは変数ではなく、アドレスという名の値だって言い張るの? 具体的にはどういう値なの? >>944 さあ? 実行するマシンが決まっているなら値はなんでもいいんじゃないんですか? それこそ文字でも記号でもなんでもいい その辺りの議論は操作的意味論の教科書で論じられていますよ >>945 なんでもいいの? 書かれているコードが>>926 だとして、 iの値が0x5617ae143260で aの値が0x5617ae143260でもあなた的には問題ないってこと? >>947 新しいアドレスにはすでに使われているアドレスの値を使ってはいけないという制約は操作的意味論でも目にするでしょう あなたが熱心に勉強するタイプの人であったなら私のレスを待たずにして自分で調べて自分で疑問を解決していただろうに残念ながらあなたは受動的にしか学習せず一生初心者のままに留まる人間なんでしょうね >>942 その通りで、単なる識別子としての「値」で解放しているだけだね そしてアロケーションライブラリによってはその「値」がアドレス自体でないかもしれない、と C言語では抽象レベルと具体化レベルがほぼ一致のためアドレスが使われアドレスで考えてもいいけど 多くのプログラミング言語ではその部分は実装レベルに隠蔽されているからアドレスで考えてはよくないね で、話を戻すと、大元の話ではその「『値で』解放している」かどうかではなくて 「『値を』解放している」のか、「なんらか抽象的な『空間を』解放しているのか」の話だったと認識してる >>948 ごめん、プログラミング歴20年超えてるんだわ まあ>>947 は意地悪だったけど、何が言いたいかっていうと、 有効なアドレスってのは実行時するかコンパイルしないことには定まらないでしょって話 でも言語仕様っていうのは、コンパイラが存在しなかったとしても存在し得るんだわ で、他の人は言語仕様の話をしてるけど、一人だけ変数じゃなくてアドレスという値だって言い張るから、 マシンが存在しない状態だとどういう値なのよ?って思ったのね 意地悪なこと書いてごめんよ おまえらって基本マジメなんやろな何か 意見の違いはあれどそんなに嫌いじゃないわ (ただし複製おじは除く)
最適化で消えるようなもんが言語仕様なわけ無いですもんなー
>>949 closeシステムコールはfile descriptorをcloseする file descriptorでcloseするとは誰も言わない file descriptorはファイルを参照する値であるがファイル自体ではない それと同様freeはアドレスという値を解放しているのであってアドレスという値で解放してるとは誰も言わない 「値で解放するの表現が正しい」(笑)って言う意見に耳を傾けるのはまだあなたが初心者を脱することができていない証拠 https://linuxjm.osdn.jp/html/LDP_man-pages/man2/close.2.html こういうサイトにも「close() は、ファイルディスクリプターをクローズする。」 とある >>951 真面目すぎてひねくれたパターンだろうな 出世もしないで片隅でコード書いてる人よくいるし >>952 その最適化で消えるようなもんが言語仕様 例えばRustでは言語仕様で通常の参照ポインタはnullにならない nullを言語仕様として扱わずNone値を持つOptionを導入にしている そしてヌルポの先へアクセスすることを完全に封じている、というのが言語仕様 ところがその抽象レベルを離れて実装レベルになると話が違う 愚直にOptionを実装すると参照ポインタ以外にメモリを余分に使う そこで最適化によってNone時は参照ポインタの実体アドレスを0すなわちnullポインタとしている これでOption分の余分なメモリを使わずに済ませている つまり言語仕様としての抽象化されたレベルと 実際にアドレスがどうなるかという具体化されたレベルは常に区別しないといけない Rustプログラマーとしては実装でどうなるかは知らなくてもプログラミングできる そしてまずはその抽象的なレベルのみ意識して学習すべき >>955 こりゃ失敬。なるほど確かにそうだわ。適当な事言いましたわ >>925 まーたオジさんいい加減なこと書いてるねw 操作的意味論ではvariableとlocationとvalueを明確に区別するのが一般的 メモリ領域のことを値だと表現するのはRust的にも操作意味論的にも間違い >>941 >Rustを含めアドレスという言語機能を持っている手続き型言語で変数と呼ばれているものはただのアドレスです 全く違うんですけどwww 都合悪くなってスレ流したいのかもしれないが とりあえず嘘八百でスレ埋め立てるの辞めてくれ >>957 操作的意味論ではそれ以上簡約できない項を値と呼びます varもlocもどちらもそれ以上簡約できないので値です 試しにTaPLで値ががどのようにBNFで帰納的定義されている集合なのか確認してみたら? あなたが理解していないだけでは? >>958 スレ埋めてるのはどちらかというとこのような無意味なレスをするあなたでは? 私は自分のレスが正しいと知っておりますのでどうぞ余計なレスを書き込まないでこのスレを延命してくださいませ 6AYXkq/Gを相手にしても無駄だから元の話に戻りましょう >>808 について自分の意見は ○「型のインスタンスに所有権がある」 ×「値に所有権がある」 ←値は途中で完全に置き変わっても構わないため× ×「変数に所有権がある」 ←変数はどんどん移動できて一時的な束縛にすぎないため× ×「アドレスに所有権がある」 ←アドレスは関数へ渡したり返したり途中で変わるため× >>961 「所有権がある」という意味が「所有権を持つ」という意味であれば 「値の所有者は変数」だから「所有権を持つのは変数」だよ スコープを抜ける時に所有リソースを解放する責任というのかownershipを持つということ 型のインスタンス? 説明してもらわないと意味分からないな >>961 > 値は途中で完全に置き変わっても構わないため 所有権というのは「後始末する責任」のこと。 内容が書き換えられることを許すのは所有権を手放しているわけではない。 >>961 > 「変数に所有権がある」 ←変数はどんどん移動できて一時的な束縛にすぎないため× 変数は移動できなくない? 何か別のことを言いたかったんかな? 変数が所有権を持つ、で良いんじゃないかな 値を変数に束縛するときに、変数が値を所有することになる そして変数が値を所有したままスコープアウトすると、値をdrop(解放)する 「変数が所有権を持つ」よりも、「変数が値を所有する」のほうがいいか
主語と目的語を逆に違う意味に誤解されるとわかったので補足します ○「型のインスタンスに対して所有権があるor生じる」 ×「値に対して所有権があるor生じる」 ←値は途中で完全に置き変わっても構わないため× ×「変数に対して所有権があるor生じる」 ←変数はどんどん移動できて一時的な束縛にすぎないため× ×「アドレスに対して所有権があるor生じる」 ←アドレスは関数へ渡したり返したり途中で変わるため× >>962 プログラミング言語の分野で一般的に用いられるインスタンスです 型とインスタンスは一般的に1対多の関係になります (シングルトンでは1対1ですが) 言語によっては型をクラスと表現する場合もあるようですがRustではそんな狭い範囲ではなく全ての型が対象です >>963 そのように値は変わっていくものだから 値に対して所有権といっても曖昧さが残るでしょう だから不変でない値に対して所有権があるとの考えはよろしくない まだ>>964 の言う変数を持ち出したほうがマシ しかし変数との関係は一時的にすぎず所有権は別の変数へ移動していきます 所有権と常に1対1の関係にあるのは(型の)インスタンスです >>964 変数が所有権を持つというのは変な解釈。 結果的にはそういう挙動ではあるんだが 値が変数に捉えられている間は寿命が継続されるというルールによって 変数のスコープと連動してるだけ。 所有権を持っているのはあくまで値。 >>967 所有権を持っているのはインスタンスであって 値はそのときどきで変化するだけにすぎない存在だと思います 俺が入れ物に対して所有権があると>>900 で書いたのも実質その型のインスタンスだ インスタンスという言葉を使うと面倒になりそうなので抽象的に型の入れ物とした いずれにせよ所有権の対象は値ではなく値が収容される入れ物orインスタンスだ 実際のところ「値」と「インスタンス」の間にそんなに意味の差はないです。 特的の型を元に作られたということを強調するときにインスタンスと言うことはありますが、 どの値も型を持つのでインスタンスではない値などありはしません。
>>971 インスタンスはその型の形をした器(うつわ)であり解放されるまで不変 値はその器(うつわ)に入った内容にすぎず可変 >>972 いいえ。 繰り返しますがそんな定義はないです。 >>967 >所有権を持っているのはあくまで値。 で、その値は何の所有権を持ってるのさ? そもそも「所有権を持つ」ってのが苦しい 英訳すると "own the ownership" になってしまうが、そんな表現は公式ドキュメントでも避けられてるように思う 値が変数に束縛されるとき、その値を変数が所有することになる 変数をreturnしたり、変数を他の変数に代入するときには、所有権がtransferされることになる ここまでは良いでしょ 例えば、公式ドキュメントにもこう書かれてる https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html > Returning values can also transfer ownership. なので、強いて >>961 の中から選ぶなら変数が所有権を持つだけど、最初に書いたようにそもそも「所有権を持つ」が苦しいので、 「変数が値を所有する」とすれば良いと思う >>965 >「変数が値を所有する」 これが正解。 >>973 皆がその点については定義がはっきりしないからこれだけ揉めてるんだろ 「そんな定義はないです」は反論にすらなっていない ああ、別に「持つ」を必ずしも "own" で訳す必要もないね さっきから変なことばかり書いててすまんね、今日は冷静になっていったんもう寝る
>>974 値自身を後始末する責任をもってる。 所有権という訳語がよくないというのはよく指摘されることだが、 何者かが値を所有しているという誤解のもとになるからだ。 変数が所有権を持っていることにしたら 一時オブジェクトの所有権はどうなってんだって話になるだろ。 >>975 >>976 そんな話は誰もしていないと思う 所有権は何に対して付随するのか生じているのかが論点 そして所有権が消滅するのは型のインスタンスが最後に解放される時 だから所有権と1対1の関係にあるのは(型の)インスタンスだと主張しています >>972 > インスタンスはその型の形をした器(うつわ)であり解放されるまで不変 いいえ。 代入で内容が書き換えられる場合もあり、 そのときに drop が呼ばれます。 寿命の管理は値に付随します。 とりあえずbookの 4.1. What is ownership? (https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html ) からOwnership Rulesの節を丸ごと抜いてきた(訳は適当) Ownership Rules First, let’s take a look at the ownership rules. Keep these rules in mind as we work through the examples that illustrate them: * Each value in Rust has a variable that’s called its owner. * There can only be one owner at a time. * When the owner goes out of scope, the value will be dropped. まずは所有権(ownership)に関するルールを見てみよう このルールを記憶に留めて以下の例示を読み進めてほしい ・Rustの各々の値(value)は所有者(owner)と呼ばれる1つの変数(variable)をもつ ・所有者は同時に1つしか存在しない ・その所有者がスコープからいなくなる時、その値は破棄される >>981 内容が書き換えられてdropすることはない 所有権と値は関係ない 値に付随するものではない >>982 に明記されているように所有権を持つ所有者は変数 所有者である変数がスコープから外れるとdrop >>982 だよなぁ。「入れ物」とか妙ちきりんな説明する人はなんなんだろう? インスタンスというのも一理ある その型のインスタンスが作られてから解放されるまで一貫して一つの存在なのに対して 変数は次々と移り変わって行く乗り物と捉えることができる そしてインスタンスがたまたま束縛されている変数がスコープから消えると乗っていたインスタンスも巻き添えで消えると考えられないこともない
横からすまんが、実際のメモリ上だと所有権ってどうなってるもんなの? >>982 にある仕組みからしたら・・・・メモリが確保されるのと同時に、併せて所有権情報(スタックへの参照か何か?)がメモリのどっか確保されるわけ? 俺、てっきりコンパイラへのただの指示だとばっか思ってたぜ 横からキターーー コンパイラの課すルールの話なので 所有権情報が実行時にメモリに確保されたりしないよ
struct S; impl Drop for S { fn drop(&mut self) { println!("drop"); } } fn main() { S; } ↑じゃあこれは何が所有権をもってて何がdropさせてんの? インスタンス説のほうがまだシックリくる? 変数も所有権を持てるしスコープ終了で手放せる?
>>961 お前が突っかかって来たんだろうが ガイジwwww 無教養のガイジども阿鼻叫喚していて草wっwr ンゴwwwwwww
lud20220923011912ca
このスレへの固定リンク: http://5chb.net/r/tech/1636247099/ ヒント: 5chスレのurlに
http ://xxxx.5ch
b .net/xxxx のように
b を入れるだけでここでスレ保存、閲覧できます。
TOPへ TOPへ
全掲示板一覧 この掲示板へ 人気スレ |
Youtube 動画
>50
>100
>200
>300
>500
>1000枚
新着画像 ↓「Rust part13 YouTube動画>2本 ->画像>1枚 」 を見た人も見ています:・Naru Sato ・Rust vs Go ・Rust part9 ・Rust part8 ・SUPER GT+ ・PUSHIM Part1 ・Rust part14 ・Rust part15 ・Rust Part6 ・Rust Part5 ・Rust Part7 ・Rust part10 ・C++ vs Rust ・Rust part12 ・Rust part6 ・Rust part11 ・Porteus Linux ・Nexus 5X Part30 ・Castle Burn ・OnePlus Part96 ・OnePlus Part87 ・OnePlus Part79 ・JUSWANNA Part.3 ・OnePlus Part81 ・Nexus 5X Part26 ・OnePlus Part94 ・Oneplus Part.4 ・Oneplus Part.5 ・Nexus 5 Part106 ・OnePlus Part46 ・Nexus 6 Part34 ・OnePlus Part20 ・Duelyst Part6 ・OnePlus Part91 ・OnePlus Part95 ・mist survival ・OnePlus Part29 ・OnePlus Part77 ・OnePlus Part34 ・OnePlus Part22 ・ZOZOUSED part1 ・BEAT CRUSADERS 79 ・IPLE HOUSE Part5 ・OnePlus Part72 ・A-Studio Part4 ・Rustアンチスレ ・OnePlus Part57 ・Android Studio 2 ・OnePlus Part10 ・iPhone6Plus Part4 ・Infected Mushroom ・Russkij Avanpost ・DE DE MOUSE☆Part3 ・Lost Frequencies ・BURST in パンク板 ・STU48専用 うたコン ・Change Request. ・CRUSTスレ Part.13 ・乃木坂46専用 MUSIC FAIR ・Houston Rockets 68 ・trap houseってなに? ・Houston Rockets 67 ・the HIATUS Part 60
01:45:07 up 22 days, 2:48, 0 users, load average: 8.52, 11.65, 14.00
in 0.063818216323853 sec
@0.063818216323853@0b7 on 020415