Rust part16
■ このスレッドは過去ログ倉庫に格納されています
>>773
そんなことすべきでない
自由にRust コンパイラによる最適化の余地を与える現在の方針がベスト
外部に公開しなきゃいけない時に外部に公開する部分だけを#[repr(C)]や#[wasm_bindgen]など指定すればよい 双方でマーシャル/アンマーシャルが必要になって無駄だよね 対C/C++はそこまで必要ならそこもRustで書いちゃうから何ら問題はない
他の言語ではそれぞれもっと大きなオーバヘッドを持っているので誤差に収まり問題にならない >>774
pubなitemのABIに最適化関係ある?
なんかと混同してない? もしかして repr(Rust) のこと言ってる? Rustだけで閉じていればpubであっても自由に最適化されるからpubかどうかは関係ないでしょう
結局Rustの外に公開する部分だけの話に限られるからそこだけ相手毎に応じる現行の方式のままで構わないでしょう C++やRustはABI決まってないのにC言語は何故ほぼ決まってるの? >>781
dylibの場合pubは大いに関係あるよ ぶっちゃけあらゆるOSがC言語で書かれているあたりCの呪縛から逃れられないよ >>782
むしろCは決まってるの?
決まってるわけじゃなくて単純だし歴史も長いから結果的にほぼ変わらない&その現状に合わせて変わらない変更をしてるだけみたいなことをgccかなんかの中の人の記事で読んだ気がするんだけどデマなんかな 近年になって作られた高速リンカ mold の作者の話でも、
文書化されていない暗黙の仕様に何度もぶつかったみたいなことだったはず。
C 以外の言語 (処理系) もツールチェインは共通のものを使っている場合は結構あるし
どれがどの挙動に依存しているかようわからんので安易に整理するわけにもいかず、
結局のところは C コンパイラとは長年に渡って協調してきたから細かい問題点が
解決されているというだけで、そんなにカッチリした仕様が確立しているわけではないと思う。 CはCPUベンダーが呼び出し規約を文書化してるよ
moldの話はELFやリンクに関連する話では
確かにABIのうちではあるけど言語ごとに異なる仕様になるようなものではないと思う AMD64の呼び出し規約をググるだけで2種類くらい出てくるのにコイツは何を言っているんだ?
>>786
呼び出し規約どころか構造体のレイアウトすら実装依存の部分があるよ >>789
そこでいう実装依存ってプラットフォームごとの差違のこと?
それとも同じプラットフォームでもツールチェイン依存でレイアウトが変わりうる場合があるの? >>791
その段階ではあまり曖昧さはない。
リンクする前の状態はリンクに必要な情報一式が入ってるはずなんだけど、
その扱いが言語や処理系をまたぐとややこしくなることもあるってこと。
アーキテクチャによって扱いを変える必要がある場合もあるし。 >>792
コンパイラがリンカに渡す情報って統一規格があるの? >>793
別に統一されちゃいないがELFとかPEとか じゃあ、そのオブジェクト・ファイル形式の仕様に問題があるってことでは? >>795
ELF に置き換わるときにオブジェクトファイルの仕様の曖昧さはほとんど解消されていると思う。
ただ現実には全てが正しく実装されているわけではなく、
場合によっては正しかったほうを間違った側にあわさざるを得ないとかいう場合もある。
仕様がどうこう言ったって、実装が間違っていたって現実にもう動いているものがあるのなら変えられんのよ。
そういう歴史的負債がどんどん積み重なってわけわからんようになる。 元々の他言語からrust呼び出す話ならそのレベルの話は関係ないでしょ
LLVMがよしなにやってくれるのでは ARM64ほどの絶対的パワーは必要ないので、ARM63で価格が120円くらいのチップになりませんかね? Option<NoneZeroUsize>などを使えば
IDやカウンタなどの用途でOption<usize>などを使っていたものを
半分のメモリサイズで済むようになるの? >>799
任せてください。符号ビット省略しておきますね Microsoftがやりそうなことだけど、Rustに独自拡張を含めたりとかしてほしくない Linuxはカーネル開発の為に今まさにRustに独自拡張を含ませようとしてるのに
Microsoftはダメなの? try_new()とかtry_reserve()とか元々ないのが不思議だったし導入の良いきっかけとなっただけ 言語自体forkして独自のエコシステムを構築しようとしなければ別に良いのでは 正直LinuxにRustなんて辞めればいいのに・・・ Rustに限った話じゃないんだけどそれなりに複雑なロジック(例えばデコーダやパーサ)の実践的なテストの
作り方の解説とかどっかにある?例えばJPEGやPNG、MP3、AVIとかを扱うようなコードを想定
関数単体のテストはともかく、結合した状態で全てのコードを動かそうとすると入力パターンがどんどん増えるし
パディングビットにゴミがあっても問題ないかなどを考慮しだすと更に膨れあがる
さらに歴史が長いフォーマットだと、そもそも仕様をどう定義するのかという点が問題になることもあるし >>803
別に独自拡張とか入れてないだろ
コンパイラへの機能追加は全部本体に入れていてnightlyで使える状態だし
Linuxカーネル向けのallocなんかは単にライブラリであって言語自体がforkしてるわけではない >>807
メディアのエンコーディングのことはさっぱりしらんけど、一般的には結合テストじゃなくて、単体テストでパターンは網羅すべし
できるんならやればいいけど、結合テストで入力の網羅なんて普通はやらない >>812
条件分岐で関数Aを呼ぶべき所を間違えてA'を呼んでいて出力結果がちょっと変・・・というのをさっきやらかした
関数そのものに問題はないし処理内容がちょっと違うだけなので実行は出来てしまうのがいやらしい
で、テストを作ろうとしたけどどうしようか悩み中 >>807
歴史があり、曖昧さが残るフォーマットの再実装はできればやりたくない仕事だな。
対応する仕様を現代で最低限必要なものを取捨選択して決め打ちで実装しつつ、考慮漏れでクリティカルなものは取り入れていく方式でやるしかないよ。
歴史あるフォーマットの曖昧な対応を追体験する作業は、不毛だからできれば既存実装におまかせすべき。 >>814
中間コンポーネントの単体テストって普通どうやるんだろ
後段のコンポーネントを切り離してテストするのか?繋げたままテストするのか?
切り離せるようにするとその部分にバグが入り込む余地が生まれるし
>>815
歴史が長いと仕様上○○であるがこれに準拠しないアプリやデータが少なからず存在するため
△△のような実装がベターだみたいなのが沢山あるからねぇ
そしてこの手の情報はググっても網羅するのが難しい >>811
Linux側にメリットがないと言ってる? >>816
> 中間コンポーネントの単体テストって普通どうやるんだろ
中間の意味がよく分からんけど>>813みたいな話なら関数A、関数A'をモックにしてテストする
たいていのテストツールではモックの呼び出し回数のテストができる >>816
JavaみたいにDIが発展しているタイプの言語だと中間コンポーネントが呼び出すコンポーネントはモックをインジェクトしてやって、適切なメソッドが呼び出されたかのテストとかよく書くね。
けど、正直Rustを含む他の言語で中間のレイヤだけ独立してテスト書くようなこだわりはあまり見たことも書いたこともないなぁ。
モジュール設計の考え方が変わるからかな? 今作っているのだとこんな感じかな?
関数C(データの前処理、処理単位への分割)
↓
関数B(処理全体の制御)→関数A'(処理1-2)
↓
関数A(処理1-1)
>>818,819
その場合モックへ切り替える機構はどうするんだろ
そのためにコードを書き換えていてはミスが入り込む可能性が高くなるし、条件付きコンパイルも同様のリスクがある
てかThe Rustのテストの所を見ても関数の呼び出し状況をテストする方法とかは書いていないんだよな
なんかその辺を良い感じに可視化してくれるツールとかあるんだろうか >>820
すまん rust だと cargo test で単体テストを実施するみたいだけど mook/stub をどうやって使うかはよくわからんかったわ
C++ とかだと googlemook とか使ってテスト用のモッククラスを作って入れ替えるかたちだね cargo testで関数テスト、モジュールテスト、モジュール間テストなどあらゆるテストをやっているけどダメなの? >>823
>>820 > その場合モックへ切り替える機構はどうするんだろ
に答えてやってくれ モックやスタブは別モジュール化しておいて
mod tests内では本物モジュールをuseする代わりにそれをuseするだけじゃないの? あと#[cfg(test)]でそれをuse
そして#[cfg(not(test))]で本物use 他の言語でもユーティリティを使わずに、DIやモックを自分でやったことがないんだろうな
説明が面倒だ >>827
テスト以外の開発の話でも
フレームワークに依存してやってる人は
単純なこと含めて本質的なことを理解してない人が多く
フレームワークなしでは何も分からず何も出来なくなってしまう例を時々見かける cfg使えば良いじゃないって人は#ifまみれで一見しただけじゃ
何がどう動くんだか判らないCのコードを正当化するつもりなのだろうか
Rustは人間が注意すれば問題ないみたいな考えはレガシーで時代遅れだ
という思想の言語だと思っているんだが違うのかな >>829
#ifは使わないし
cfg(test)はテスト分離のため必須でしょ
どんな環境でも魔法は無いよ >>829
cfg使わないで済むいい方法があるなら書いてよ... mod tests に cfg(test) は必要だとして
依存性の注入にはtrait使えって事なのでは traitで置き換え可能にするのが面倒というのはありそうだな。 AMD64のデフォルトのオペランドサイズは32bitなのにusizeが64bitなのは何でなのかな >>834
usize はポインタのサイズということになっている。 >>831
単体テストで、依存を分離するのは当然のことすぎてみんな説明が億劫になってる
C++だろうがRubyだろうが、モックやスタブを使って、関数同士やクラス同士の依存を切り分けてテストするのは当たり前
そうしないとそもそも単体テストにならないじゃん
わかってる人にしかわからないであろう簡略な説明をすると、テスト用のエントリポイントで、テストに使うモックオブジェクトを指定するだけだよ
そういうことができるようにあらかじめコード設計しておかないといけないがな
考えてなかったならリファクタが必要 本物と異なり決まった値を返す送信元スタブと
本物と異なりassertだけする送信先モックを
mod testsの中では本物の代わりにuseするだけだよね
入れ替えちゃうからtrait制約で本物も偽物も受け付け対応とかわざわざする必要ないよね >>839
useしたモックをどうやって注入すんの
関数の引数もstatic変数でも良いけど、テスト対象の実装がモックも本物も選択的に使えるようにするならば、
genericな型を受け付けるような実装にしておかないといけないのでtraitが登場するのでは
それともmod testsの外もcfgで置き換えると言っている? 要は
use imp::Foo;
fn target(foo: Foo) {}
がテスト対象だとして
mod tests {
use mock::Foo;
#[test]
fn test() {
target(Foo::new());
}
}
してもコンパイル通らないよね
targetがimp::Fooもmock::Fooも受け付けるようにするにはtraitが必要では >>842
他の言語は他のやり方でやってるだけだろ u32 を格納する型が必要になり、また、逆に u32 に変換する必要もあるという状況で
せっかくだから u32 に変換可能な型は受け入れようと考えてこんなコードを書きました。
しかしエラーになります。
struct Code(u32);
impl<T: Into<u32>> From<T> for Code {
fn from(x: T) -> Self {
Code(x.into())
}
}
impl From<Code> for u32 {
fn from(Code(x): Code) -> Self {
x
}
}
結果的に自分自身への変換を許すことになってしまうのが既存 (標準ライブラリ)
の定義と衝突しているという理屈は理解しているのですが、
問題を解消するためにこの定義が受け入れる範囲から自分自身 (Code) は除外するように
うまく制約を付ける方法は思いつきません。
そもそもこんなところで勝手に変換するのがよくない作法だとかそういうのは脇に置いて
「自分自身だけ除外するような制約」を上手いこと表現できませんかね? 初歩的なことですまんけどさ
メソッド内で↓みたいなのってよく見るけど、こう言うのってself.asdfのまま使用するのに比べてどういった利点があるの?
let asdf = self.asdf; 書き方は違うけどフィールドそれぞれに対して処理を行う場合に抜け漏れがないことをコンパイラにチェックさせる目的でローカル変数にすることはある
let Foo { foo, bar. baz } = self;
としておくと後続の処理で使わないフィールドがあったときにコンパイラが警告してくれる
構造体に新たにフィールド追加した場合も分割代入の箇所でコンパイルエラーになるので修正必要箇所を洗い出すことができる 本人は俺ってスゲー、天才やん!
って思ってるんだろうけど後でコード見たらなんでこんなイミフなことしてるんだ?バカじゃねーの
ってなるパターンかと
まあこういう工夫をすること自体は悪くない フィールドそれぞれに処理をするシチュエーションがわからない >>851
俺もそのdestructuring assignment自体は使いまくる
しかし目的が漏れチェックとは違うのでこうだな
let Self { foo, bar, .. } = self; こうやって自己満足の意味不明なコードが量産されていく 全フィールド舐めるのが重要な処理ってシリアライズとかだろうか
そんな小手先のテクニックとかじゃなくてproc_macro組んだ方がいいと思う
シリアライズしたいだけならserde使って#[derive(Serialize)]
これも結局proc_macroだわな コマンドライン引数や設定ファイルの定義をclap::Argやserde::Deserializeで宣言的にやって、
それらを処理するところで分割代入してローカル変数にして処理してる
人間が意識的に気をつける必要がある箇所を極力減らしたい気持ちでやっている
好き嫌いあるかも知れないけど趣味プロダクトだしコーディングの意図をコメントに残してるから許せ >>844
impl<T: Into<u32>> From<T> for Code {}の定義はFromの反射性と衝突するから間違ってる。
Into<u32>を受け付けたいなら関数のパラメタの型をT: Into<u32> or impl Into<u32>にすればいい。
まあ、実装上の規約として必要なんで内部ではtrait IntoFooはパターンとして使われるけど外に漏らすようなものでもない。 アドレスを考えれば明白に別物
一方で
let t = (123, "abc");
let (x, y) = &t;
と自動マッチングしてくれて
&t の型は &(i32, &str)
x の型は &i32
y の型は &&str
となる
つまり&(T, U)が(&T, &U)に分割代入される amd64ターゲットでアセンブラリストを吐かせてみたらr13が全く使用されていないんだけど
r14、r15よりr13を空けておく理由がなにかあるのかな うまく表現できないのですが、cやc++なら部分から始められる(動くものが作ることができる)のですけど、rustはそんな気がしないというか
伝わりにくいかもしれませんけど そういう事象をちゃんと論理がとおった表現ができないからrustが使えないんだよきみは! 作りたいものの設計のイメージがc++でできているならそれをrust化するのは比較的簡単だろうしそれができないならrustの基本的な理解が足りないだけかと Rustはデータ構造を最初に設計しないといけないというのはあるな
C++でもちゃんとそういうやり方が出来てるなら素直に移行できるだろうけど
雑にポインタ持ち回ったり実装の都合でアドホックに相互参照入れちゃったりする人には厳しいだろう C系は良くも悪くも動いてしまうんよな
そんで知らぬ間に副作用まみれになっている 雑に始めてから整理していくスタイルなら C++ のほうがやりやすいというのは理解できる。
でも雑に始めたら整理する機会などないのが現実。 >>867 ですが、
部分から始められるというのは、部分的な学習からということです
ここまで学習すればここまではできるとか
rustでは最初のプログラムを作るにもたくさんのことを知らなければならないというか
Haskellをかじったことがあり、とても興味深いのですが
わかりにくい独り言に、レスをくださってありがとうございました ■ このスレッドは過去ログ倉庫に格納されています