Rust part13
レス数が1000を超えています。これ以上書き込みはできません。
乙
シャドーイングが困るのって関数がよっぽど長いときだけなんで問題にならんよね >前スレの質問「間違って同じ変数名つけたコード片突っ込んでしまった系だと他言語にあるらしい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ガンダム乗りなのに連邦の白い悪魔以外は認めないんだ
どっちでもいいじゃんか・・・
https://i.imgur.com/a9pC9PS.jpg 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());
} オーバーフローしないように型を選択することとか、
汎用的に使えて問題を避けることを考えたら平均を計算するのってそんなに簡単な話ではないと思う。 stats::median
今でもfilter使えだとかうっせーのばっかり、標準ライブラリは小さくて良いYO
https://lib.rs/science/math
statrs-dev/statrsとか好きなの引っ張ってきて入れろよ?平均じゃなくてもさ 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 が要求されているでよ やりたいことってこんな感じ?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f97e7a0bd7775b88065da30bcf6a6a01 >>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);
}
} https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3702e2291f04ef5e40bd5310079972b1 >>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 >>118
timesより>>112のm..n rangeイテレータが汎用的でいいんじゃね? >>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++の規格のどこを参照すれば定義されているのか詳しく 規格の上ではまだ見つけられないが
https://timsong-cpp.github.io/cppwp/n3337/over.match.oper#tab:over.rel.op.func
member function
non-member function
という当然の名称が出てきている
日本語版の規格分かる人「フリー関数」とやらを引用してくだしあ その「フリー関数」とはクラスのメンバー関数ではない非メンバー関数として
クラスのない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));
}
} 第一引数がselfである関数(メソッド)を自由関数として呼び出すことはできるけどその逆は真ではないってことじゃないかな。
https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax メソッド呼び出しできない関数は「フリー関数」ってことでええんちゃう?
非メンバ関数って呼ぶほうが断然一般的だとは思うが >>197
selfがIteratorだからクラス志向だとprint_all()はIteratorクラスのメンバーかな
しかしそのPrintAllはIteratorを継承してないから難しい https://dev.to/mindflavor/rust-builder-pattern-with-types-3chf
この手のPhantomDataとジェネリクス使って特定の条件満たした状態じゃないとbuildとかのメソッドが実装されないようにしたりしたビルダーパターンってここの人たちはどう思ってる?
説明だけ見るとめっちゃ良いやん!って思うけどいざ自分で書くとメリットに対して無駄に複雑&使う側としてもエラーが見にくいとかなんか微妙に感じちゃう >>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と呼ぶべきものではないと思う https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ebfd22dd62614e127bb3dbf0e57e5651
sもmutにしてDerefMutもimplしてるのになんでgの方は通らないんでしょうか? >>217
もっと単純に let z: *mut i8 = *s; も通らないので
これはDerefMutの条件であるmutable contextを満たしてないのではないか
そしてもちろん let z: &mut i8 = *s; は通るし **s = 88; も通るからDerefMut自体は機能している PR出てるけどどうなるかわからない
https://github.com/rust-lang/rust/pull/86647
現状は
let s = &mut *s;
g(*s);
するか
g(&mut **s);
するか >>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);
}
}
} ブロック式最後の式の値は、ブロック式の値として返るので drop されるタイミングがブロックの末尾よりも後の箇所になるということかな
for のブロックは値を返さないけど、ブロック式と同じ扱いをされているっぽい
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b5639623c9a0dff8426f1d38b7123fdc >>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トレイトは無いんですか? >>231
詳しく知りたければまずはここ↓だけど
https://doc.rust-lang.org/reference/destructors.html
言語自体をいじりたいわけじゃなければ
あんまり深追いしても役に立たないよ >>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
https://www.youtube.com/watch?v=o6PuxDr8Meg >>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()をおながいします >>411
「多数あり外部で十分派」なのに>>412には答えられないの?? 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
コンパイル時にチェックされるものと実行時にチェックされるものがあるよ
多くの場合は前者で済むけど、可変参照を複数箇所で共有したい場合は後者が必要になる キモいと思うレスは黙ってNGしといてこれやるといいよ
https://dtolnay.github.io/rust-quiz/
Rustの文法はもうバッチリと思ってる人向け
重箱の隅をつつく問題なんだけどすごく勉強になる >>475
コンパイル時のチェックって結局人手で書いたコードによるチェックなんでしょ?
ということは担保は単体テストってこと? そもそもだかマシン語でみりゃ
メモリに型もなんもないよw
単なるバイトだらけ
コンパイルの段階で変なコードか
けないようにしてるだけ
でもこれだとぬるぽ!が回避できないので
仕方なく仕組み入れたのがRustでしょ Rust書くときは常にIDEに直してもらいながら書いてるから>>476みたいなの全然解けないわw >>476
確かにこれは勉強になる
知らなかったことばっかり Rustのメモリ安全って、null安全のことだったの?しょぼ >>479
コンパイラのコードのこと?
安全性の保証に形式的証明を与えようという取り組みはあるけどコンパイラの実装が正しいかはコンパイラのテスト頼りだね
https://plv.mpi-sws.org/rustbelt/popl18/ >>486
初心者向けにわかりやすく解説しようという試みは評価するが
間違いが多すぎて萎える
ここで初心者の質問に回答してる初心者と同じ >>488
整数型や浮動小数点型といったスカラー型の変数は、変数間の代入において所有権は基本的に複製されます。これを「所有権の複製(コピー)」といいます。変数間の代入などにおいて所有権は複製されるので、値の所有者は常に1個でなければならないというルールは守られます。 >>489
正しいこと言ってるように見えるけど
どう間違ってるの? 所有権が複製されるって表現はなんかRcを想像してしまうな 整数型とかの単純のヤツはCopyだかCloneだかをderiveしててデフォのmove semantics機能してなかった気がするな
最近書いてねぇから忘れてんなこれ(´・ω・`) 数値型が特別扱いされているわけではない
Copy traitを実装すればcopyされる
Copy traitを実装しなければmoveされる Copyで複製されるのは所有権じゃなくて値だよね
代入先の変数が複製された新しい値の所有権を持つことになるけど、所有権が複製されるんじゃあない copyだけでなくmoveされるのも対象は値
所有権ではない
値をmoveした結果
値に紐付く所有権がくっついてくる 値と同時に所有権 "も" copy/moveされると捉えることもできるのでは 個人的に間違った捉え方をするのは自由だが
それを初心者に広めるのはやめていただきたい あんたらのお仲間他スレで暴れてばかりだよ、引き取りに来い 所有権が複製できちゃったら「一個の値に一個の所有者」ってルールが守れないやん(´・ω・`) >>492
> 所有権が複製されるって表現はなんかRcを想像してしまうな
https://doc.rust-lang.org/std/rc/index.html
> The type Rc<T> provides shared ownership of a value of type T
shared ownershipという表現でうまく説明できててrustっていいなと思う というか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が集まる駄スレ >>515
> そこよく勘違いされてるけど値自体はCopyトレイトと関係なく常に複製されてるよ(最適化で除かれるとかは別として)
>
> https://doc.rust-lang.org/std/marker/trait.Copy.htmlに全部書いてあるけど
へー、なるほど、いろいろ納得したわ >>518
だよなぁ。
誰か筆者に指摘した?
あと、「所有権の複製(コピー)」はRustだと実際は何て言ってるの? 「所有権の複製」でググってもほぼ誰も使って無い用語で草 所有権だけをすることなんてないだろうし、そんな言葉なくね
そもそも意味わからん
処理系を実装してる人にとっては考えることかもしれんが まあ無駄にややこしいだけの説明だわな。
これで間違ってない!とか意地張るような輩は俺だったら落とすわ。 どっちに解釈しても問題ない話の一方に固執する方がお断りだな。 所有権の複製の意味が分からん
不用意に独自用語使うのを野放しにしちゃいけない
専門用語とその定義がなぜあるのかを考えて欲しい >>519
実際に生ポインタ(アドレス)を見る形で実験したところ
「関数に値渡し(≠参照渡し)する」時は
「Copy trait実装の有無」に関係なく
「必ず値は複製されている(=別アドレスになる)」という
よく考えれば当たり前の挙動となった
つまり話をまとめると
・Copy trait実装の有無と関係なく「値は必ず複製される」
・Copy trait実装がある時は「所有権も複製される」
・Copy trait実装がない時は「所有権は移動する」
これ以外に表現しようがないことがわかった メモリ上の動きと言語上のルールの違いもまともに理解してなさそうな連中だな。 mutの時に書き換えると元の値と別の値に出来るのだから新たな所有権が生じているのは事実
あとは、所有権の複製、という表現の問題
間違ってはいないが理解しにくいならば別の良い表現で置き換えればよいが何がいいだろう? ムーブの場合は元の所有権が破棄されて新たな所有権が生成される 値を複製したらメモリの領域が別なのに「所有権の複製」は何を複製すると思ってんだろ >>532
さっぱりわからんよな
>>529
> 間違ってはいないが理解しにくいならば
何言ってんのコイツ? 全く同じものを新しく生成するのと複製は外から見て区別しようがないんだから 所有権はコンパイル時のはなし
メモリの動きは実行時のははし 現実世界の所有権が複製可能な権利だったのなら
複製されると勘違いする人がいても不思議はないが・・・ 質量保存則に縛られる現実世界の複製との対比には限界があるわな。 権利のような無形物は質量保存則には縛られない
Borrow Checkerが課すルールをわかりやすく説明するためのメタファーとして
現実世界の所有権という概念を借りてきてるのに
そのメタファーを分かりにくくするエクストリームなルールを勝手に追加して広めるのはやめよう というかRustの実装のところ見ればいいんじゃね?
仕組みのコードはあるだろうし https://doc.rust-lang.org/rust-by-example/scope/move.html
ここでは所有権が移動される(the ownership of the resources is transferred)とあるから
ここからの類推で所有権の複製という言葉が出てきたのかな 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される」そして「新たな所有権が生じる」 そんな複雑な理屈じゃなくてただのバカが意固地になってるだけだろ。。 >>602
moveの対象も値だぞ
the bookのownershipの解説ページに1つでもmoveの対象がownershipになってる箇所あるか?
https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html
@ITの記事はそこも間違ってる >>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()); メソッド名が動的←→静的と対称的であったりもしないし・・・
Cell: get, get_mut, set
RefCell: get_mut, borrow, borrow_mut
ただしget_mutはそれぞれ
https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get_mut
> this method expects self to be mutable, which is generally not the case when using a Cell.
https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.get_mut
> this method expects self to be mutable, which is generally not the case when using a RefCell.
とあり通常の用途とやらで考えると
Cell: get, set
RefCell: borrow, borrow_mut
共に"Cell"で内部可変性を表現しつつ
用途はメソッド名で十分わかるしまぁもういいのかこれで >>632
>>636
Cellっていう短い名前にクッキリした役割を描いたのはRust陣営は成功かもね
Rust以前に前例があるかどうかは知らんけど
>>634
名前の説得力が不足してる疑いはあるよね
>>635
それなw >>637
Cell⇔RefCell は 静的⇔動的 の関係ではない
Cell⇔RefCell は 直接⇔参照 の関係
そのため余分にRefが付く
静的⇔動的 の関係にあるのは
「&」⇔「RefCell::borrow()」)
「&mut」⇔「RefCell::borrow_mut()」 >>639
少なくとも構造体の説明としては
https://doc.rust-lang.org/std/cell/struct.Cell.html
> A mutable memory location.
https://doc.rust-lang.org/std/cell/struct.RefCell.html
> A mutable memory location with dynamically checked borrow rules
↑のように書いてあるね
だから
SCell // A mutable memory location with statically checked borrow rules
DCell // A mutable memory location with dynamically checked borrow rules
こんなんでもよかったんじゃないかなぁと思ったがこれはこれで石投げられそう >>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に対してのみ上述の参照借用規則が適用される >>641
なるほどね
https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get_mut
> If you require interior mutability by reference,
> consider using RefCell which provides run-time checked mutable borrows through its borrow_mut method.
↑ここでもまずは「参照による内部可変性が必要な場合は」と先に来てるね
「実行時に動的に借用のチェックをさせたいという」のが動機だと俺勝手に思いこんでたけど
それは一番じゃないみたい >>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に関しては同じ振る舞いでデータの場所がヒープか否かの違い
全然関係性違うと思うんだけど >>653と>>649は合っている
>>650は間違っている
BoxとRefCellには共通点も類似性も関係性も何もない 「所有権の複製」がおかしいってことは納得言ってくれたん?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のはずなのに波線の指摘とかコード補完とか効いててびっくりしたわ
https://www.youtube.com/watch?v=MsocPEZBd-M
うちではこんなのならないんだけど・・・・やり方わかる人いたら教えてくれんかな? Repl.itは、リンターやデバッガーからサードパーティのパッケージ、ホスティング、デプロイまで、
すべてを備えた、ブラウザー内の完全な共同クラウド開発環境です コンパイル時に判明しないゼロ除算やオーバーフロー除算 ex. -128_i8 / -1_i8 はpanicとなるので
それが困る場合はchecked_div()を使えばOption型が返りNoneとなる >>688
ほんとだ、定数式じゃなくてもエラーになるんだね
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=78efbd7a2e6ac42c15da2cfb0d11485f
即値じゃなくてもエラーにしてくれてある程度賢そう
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=381372577a28207f3a37bc007ac6e29c 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を返しエラーが判明する finishの呼び忘れを静的に捕捉したいということだからflushの例では不十分かな
add_entryの戻り値型の暗黙のdropを防げばいいけど、そういった機能はまだない(RFCにはあるけど進んではない)
https://users.rust-lang.org/t/prevent-drop-at-compile-time/20508
このスレッドではdropの実装に存在しないC FFI呼び出しを書いておいて、リンクエラーとして捕捉する方法が提案されているね >>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な型が実装できるようになったとしても所有権は複製されないから >>768
「currently disallowed」「current implementation is incorrect」「disabled for now」と強調されてるように、
あくまでも現在の実装は本来とは異なり正しくなくて、問題を引き起こすために、CopyとDropの両立を現時点では禁止してる。
理論的にはCopy型は複製分岐されて各々がdropされる形が正しくてCopyとDropの共存が可能。
この暫定的な実装に引きずられた暫定的な仕様が、将来は正される可能性も残す表現となっている。
両立禁止で実害が出てないため優先順位は低いと思われるが、もし将来に共存可能になったとしても後方互換性は生じない。
>>772
最新のエラーメッセージで合っている。
https://github.com/rust-lang/rust/blob/master/compiler/rustc_error_codes/src/error_codes/E0184.md
Latest commit 9e5f7d5 on 28 Aug 2020 >>773
>理論的にはCopy型は複製分岐されて各々がdropされる形が正しくて
複製分岐されるのが正しいという根拠は? >>774
Copyなので複製分岐されるのは当たり前。
今はそこが論点ではなくて、複製分岐の後に各々に解放処理が生じるけど、現在は正しく実装されていないので仕様に制限があるとコンパイラのメッセージでも出る話。 >>774
複製分岐おじさんは日本語通じないからまともに相手しても時間の無駄だよ いつも傍から見ていてパターンがわかってきた
普通は「○○○は間違っている、×××が正しい」となるんだけど
なぜか「○○○は間違っている」だけで終わって代わりとなる対案が出てこない
>>771のように「勝手な思い込みの妄想で嘘」と否定するだけだったり
>>774のように「正しいという根拠は?」とこれも同じパターン
全てに共通するのは対案を出さずに否定ばかりしている言動 >>775
今は複製分岐されるのが本当正しいと言えるのかどうかが論点です
論点をずらさずにそう考えた根拠を示して下さい
ありませんか? >>777
仕様化されてるよ
エラーメッセージをアップデートすべき
https://doc.rust-lang.org/reference/special-types-and-traits.html#copy
その時代とはDrop周りの実装も大きく変わってる上に
CopyとDropが排他的である前提で書かれたコードも多々あるから
Copy+Dropな型をサポートするならエディション対応が必須 >>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にします 単なる揚げ足取りでしょ
本人もよくわかってないと思われ >>799,802,806
799は言葉足らずだと思う。
>>808 のがより合ってる。 あちこちでrustおじさんが暴れてるんだけどこのスレでも暴れてんのな >>808
その2つの解釈に何の違いがあるのってことだよな 論理性がない、客観性もない、実績もないのに上から目線の奴いるよね
素人相手にマウント取りたいアフィブロガーやアフィチューバーの類なんだろうけど >>812
「言葉足らず」だっだという事にしたいのねw 所有権を実装の観点からだけ見た場合はCopy型に所有権は(設定されて)ないと考えるのは妥当
説明用の概念として見た場合はshared referenceを除くCopy実装型にも所有権があってコピーされた際にその値に対する新たな所有権が発生すると考える方が妥当
公式は基本的に後者 >>808
「所有権が複製される」という言い方は
単なる表現の問題として矮小化されるものではなく所有権という概念を根本的に誤って解釈してるのが大きな問題
それを吹聴するのはRust入門者の学びを妨害する行為なので叩かれてる >>818
所有権という概念はどう解釈すべきなの? https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html
Ownership is a set of rules that governs how a Rust program manages memory.
rust の定義では ownership はルールのことを指している?
https://dictionary.cambridge.org/ja/dictionary/english/ownership
the fact that you own something
辞書的な定義ともまた違うようだ 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 で正しいの書いてくれてるのに、公式でもそうなってるのに何故に頑なに所有権の複製を広めようとしてるのよ笑食べて 語感には個人差があるからな
個人的には「複製」といえばCopyじゃなくCloneだし、Cloneなら「所有権の複製」もぎりぎり許せる
Copyを無理やり日本語にするなら「複写」かな
Copyの何たるかは
https://doc.rust-lang.org/std/marker/trait.Copy.html
で十分説明されてると思う
>>881
「ファイルの所有権がある⇒ファイルにアクセスできる」
の類推で
「変数(値)の所有権がある⇒変数(値)にアクセスできる」
と誤解されるケースはたまにみかける
これは用語(訳語)の問題でもあるから多少は仕方ない 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?
(ttps://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 このスレッドは1000を超えました。
新しいスレッドを立ててください。
life time: 96日 22時間 39分 19秒 5ちゃんねるの運営はプレミアム会員の皆さまに支えられています。
運営にご協力お願いいたします。
───────────────────
《プレミアム会員の主な特典》
★ 5ちゃんねる専用ブラウザからの広告除去
★ 5ちゃんねるの過去ログを取得
★ 書き込み規制の緩和
───────────────────
会員登録には個人情報は一切必要ありません。
月300円から匿名でご購入いただけます。
▼ プレミアム会員登録はこちら ▼
https://premium.5ch.net/
▼ 浪人ログインはこちら ▼
https://login.5ch.net/login.php レス数が1000を超えています。これ以上書き込みはできません。