🤮Rustが嫌いです。2025/04/08に公開2025/04/1018件RustポエムWebAssemblyWebRTCideaDiscussiontodesking2025/04/08 「メモリ安全な言語」なのに、静的文字列を生成するために意図的にメモリリークを起こすBox::leakを使わざるを得ない皮肉。これが「直感的」で「安全」なコードの書き方なのか? BuildInfoのメンバの型を &'static strではなくStringにすればリークさせる必要は一切ないと思うのですが、あえて &'static str を使わなければいけない事情があったのでしょうか……? あと記事の趣旨とはそれますが、このBuildInfoという構造体はなんの用途で使っているのでしょうか? ビルド時間としてプログラム実行時の現在時刻が指定されているのはおかしい気がする。 パラッコリー2025/04/09 BuildInfoのメンバの型を &'static strではなくStringにすればリークさせる必要は一切ないと思うのですが、あえて &'static str を使わなければいけない事情があったのでしょうか……? おっしゃる通りです!これは完全に私の設計ミスでした。「静的文字列参照を返す関数」という固定観念にとらわれていました。ご指摘いただき、ありがとうございます。 あと記事の趣旨とはそれますが、このBuildInfoという構造体はなんの用途で使っているのでしょうか? ビルド時間としてプログラム実行時の現在時刻が指定されているのはおかしい気がする。 こちらも本来ならbuild_dateはビルド時の時刻を示すべきで、実行時の時刻ではおかしいですね。正しい実装であれば、ビルド時の情報を埋め込むマクロを使うべきでした。 pub fn build_info() -> BuildInfo { BuildInfo { version: VERSION.to_string(), // コンパイル時の時刻を埋め込む build_date: env!("BUILD_DATE").to_string(), commit_hash: option_env!("GIT_HASH").map(|s| s.to_string()), rust_version: rustc_version_runtime::version().to_string(), } } この構造体は主にログ出力やアプリのAbout画面、デバッグ情報表示に使っていました。実際にはビルドスクリプトでコンパイル時の環境変数を設定し、それを埋め込むのが正しいアプローチですね。私の設計と理解の問題だったと認めざるを得ません。 返信を追加chicuwa2025/04/08Rustを趣味のプログラムで使っているRust信者です。 ビルド時間の部分は特にすごくわかりますね。本当に遅いし、依存関係周りで問題が起こると本当に分かりづらい。本当に...。ただ、Rustは実行時の速度とビルドの速度を天秤にかけて実行時の速度に重きをおいているという点は補足しておきたいです。プログラマーにではなく、ユーザーに優しい言語です。 あと、デバッグが大変なのもわかります。wasmはまだ触ってないのでprintデバックすら厳しいのは知らなかったですが、スレッド内のパニックとかも要因がなかなか掴めなくてしんどいです。ここはネットで検索する限りRustの弱点として言われてることが(観測範囲では割と)多く、解決策らしい解決策も少なくともWindows環境では知らないです。Linux、macだと割とあるらしいですが。 所有権やライフタイムも初心者が苦しむところですね。というか自分も'a、'bとかを指定するときはまだ苦しんでいます。ただ、基本的にBox::leakを使わないといけない時点で設計のほうが間違えてるのでは?と自分なら考えますね。オブジェクト指向プログラミング並に結構プログラムの考え方自体を変えないといけない部分がある言語なので。このあたりが学習難易度を上げている要因だと思います。Rustには思想がくっついています。なので、基本的に「嫌なニオイ」がしたら、自分が間違えていて思想に則った方法があるはずだという考えで進めると割とRust君は怒ってきません。優しい子になってくれます。 Rustのエコシステムは本来、Rust内で閉じてさえいれば問題が起こったことがないので、「Rustで完結していれば」本当に優秀なエコシステムだと思っています。ただ、C/C++だったり、wasmだったり外部の連携をしようとすると、しんどいことはあります。ただこれはある程度仕方ない部分が大きい気がしています。外部の側が悪いこともありますし、そもそも全く別の言語同士をつなげること自体に少し無理がある気がするからです。 マルチプラットフォーム対応はしたことがないのでわかりませんが、Rustライブラリのexampleを見たときに#[cfg]の嵐で面食らったことはあります。ただ、トレイトかEnumを使って抽象化すればある程度#[cfg]の嵐は防げるのかな...?ここは本当に詳しくないのでなんとも言えませんが。 エラーメッセージは好み分かれますね。たまに長すぎて読む気の起きないやつはあります。特にトレイト系は。ただ、基本は必要十分だと思います。VSCode使ってると、ざっくりとしたエラーだけも確認できるので、必要だったら見るという感覚でした。 それぞれの章に言いたいことがあったから全部書いてたらめっちゃ長くなっちゃいましたが、言いたいことはこんな感じです!改めてRustについて考えさせてくれる良い記事をありがとうございます!やっぱり、批判なしに良い言語コミュニティは作れないので。 返信を追加prbt2025/04/09ぱっと見クローンで躓いているようなので所有権がまだ分かっていないのかなと思いました。 メモリ安全性等のメリットはあくまでC++比較ということでその他のGCを使っている言語からすれば複雑なことをひたすらやっているように感じます。仕方のないことです。 正直個人で使うメリットは薄いです。大規模開発で保守性が活きてくるようなイメージです 返信を追加Toru32025/04/09に更新[profile.release] lto = true codegen-units = 1 opt-level = 3 panic = "abort" strip = true この設定ですが意味を理解して書かれてます? LTOはリンク時最適化なので有効にすればビルドは遅くなります。 codegen-units は最大何並列でコード生成するかなので1にすれば当然ビルドは遅くなります。 返信を追加すずねーう2025/04/09 コントラストとして、Node.jsのエコシステムでは: # 通常はこれだけでOK npm install npm start 過去に node-gyp のエラーで2日無駄にしたので、Rust エコシステムと npm だけで解決してる方が良くないすか?と思ってしまう 返信を追加タコ2025/04/09過激な言葉で批判している割に、所有権やライフタイムを全く理解しておらず、出鱈目で無知を晒しているだけの面白みに欠ける記事だと思いました。 同じ機能をTypeScriptやPythonで実装していたら、おそらく半分の時間で2倍の機能を実装できていただろう。 TypeScriptやPythonで済むのであれば、Rustを使う必要はないでしょうね。でも、TypeScriptやPythonしか書けない人は、それらのインタプリタを修正することすらできないでしょうね。 CPythonのコードを読んだことありますか?自分でメモリの確保と解放をしなければならず、少し注意が散漫になったら不正なコードを書いてしまいそうになります。Rustの所有権システムは、CやC++で人間が注意しなければならなかったことをコンパイラーが代替するものです。 返信を追加makoto-developer2025/04/09慣れるとRustのエラーメッセージは親切にしかみえなくなる。 たしかに学習曲線は中級者まで角度が高めかもですね。私も最初は本を読みながらやってたけどバージョンを同じにしてもビルドで動かず結構詰みました。手前味噌ですがRustをやる前にC++を学んだほうが良いかもしれないです。私は遠回りしてC++から入ってRustを学びました。なぜRustが誕生しなければならなかったのかを体験することでありがたみを理解できました。ここはやはりメモリ管理の地獄を渡り歩く経験をしておくとRustの所有権がなんて素晴らしい仕組みなんだとありがたみを知ることで恩恵を受けられます。何のアドバイスにもならないですがRustはただ速いとか安全とかだけで踏み込むとそこはジャングルですぐ帰りたくなるかもしれないそんな気持ちは確かに理解できます。 なんかおすすめの本とかないかな。結局私は公式のチュートリアルとRustのソースコードを読んで乗り切ったのでなんともですが良いのを見つけたらここに書きますね。 返信を追加ぶんちん2025/04/09うーん、Cを学びましょう 返信を追加EdamAmex2025/04/09Rustで書かれたDenoは実質RustなのでDeno使おう!(?) Hidden comment返信を追加dalance2025/04/09に更新#[cfg] については記事中にあるように細かい単位で切り替えようと思うと大変なので、ファイル単位くらいにすると楽かもしれません。 // system.rs // 実装を切り替えつつ、各実装のシンボルをエクスポートする #[cfg(target_os = "windows")] mod system_windows; #[cfg(target_os = "linux")] mod system_linux; #[cfg(target_os = "windows")] pub use system_windows::*; #[cfg(target_os = "linux")] pub use system_linux::*; // system_windows.rs pub struct System {} // system_linux.rs pub struct System {} // main.rs // systemからエクスポートされたものを使えば各プラットフォーム毎の実装になっている let system = system::System {}; WASMは大変ですよね…。Rustのエコシステムの中でも結構ユーザ体験の悪い部類な気はしています。 「CLIやサーバサイド向けに書いたコードがこの程度の手間でブラウザでも動く」という観点では結構ありがたいのですが、 ブラウザをメインターゲットにするならやはりJS/TSが楽だろうと思います。 返信を追加r-sugi6ヶ月前に更新色んなコメントがありますが、個人的に面白かったです! 学習コスト高いのに、(Web系の)フリーランス案件数が少ないからRustやることはなさそう。 リントとかのツール系ではお世話になっています。 返信を追加まぬけのさひろ2025/04/10に更新いろいろありますが誰も指摘してない点を1つ メモリリークはRust的には「安全」です https://doc.rust-jp.rs/rust-nomicon-ja/meet-safe-and-unsafe.html https://doc.rust-jp.rs/rust-nomicon-ja/leaking.html 返信を追加kkent0303152025/04/10に更新Rustについては、所有権の概念というより、それに基づいたデザインを考えることが最も難しいとされる部分なのではないかと思います。 もっとも、BuildInfoについては、Rustらしい書き方をすることで解決すると考えます。 この場合、すべてのフィールドはビルド時のメタデータを含むので、ランタイムで処理を行う必要はありません。 すべてのフィールドを静的ライフタイムとして定義するのがパフォーマンスの観点からしても適切な実装です。 Cでは、明示的に定義実装しない限り、すべての文字列は基本的に静的なライフタイムを持ちます。このRustプログラムでは、意味論的には等価のことを行っています。 [build-dependencies]はビルド時のみに必要な依存関係を解決します。これにより、製品バイナリに不要なコードが含まれることを回避します。 Cargo.toml [package] name = "buildinfo" version = "0.1.0" edition = "2024" [dependencies] [build-dependencies] chrono = "0.4.40" rustc_version = "0.4.1" BuildInfoはライフタイムを明示的に指定することでもっと安い実装に置換できます。Stringは基本的にヒープを使うので、ビルド時メタデータに使用するには高価すぎます。 main.rs #[derive(Debug)] pub struct BuildInfo<'a> { version: &'a str, build_date: &'a str, commit_hash: Option<&'a str>, rustc_version: &'a str, } さらに、build_info関数はconst関数にすることができます。これにより、関数はランタイムではなくビルド時に評価されます。 main.rs pub const fn build_info<'a>() -> BuildInfo<'a> { BuildInfo { version: env!("CARGO_PKG_VERSION"), build_date: env!("BUILD_DATE"), commit_hash: option_env!("GIT_HASH"), rustc_version: env!("RUSTC_VERSION"), } } env!マクロを使用するこれらの環境変数はビルド時に定義されている必要があるので、build.rsを用意します。 これはビルドスクリプトと呼ばれるもので、cargo buildを実行したときに実行されます。ここで、必要な環境変数をセットします。 このスクリプトはビルド環境でのみ実行され、ランタイムの実行バイナリに影響を与えません。 build.rs fn main() { let build_date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); println!("cargo:rustc-env=BUILD_DATE={build_date}",); println!( "cargo:rustc-env=RUSTC_VERSION={}", rustc_version::version().expect("Unable to retrieve rustc version") ); } この方法でビルドしたバイナリは非常にクリーンで、全体を通してパニックフリーです。 main.rs fn main() { let info = build_info(); println!("Build info: {info:?}"); } オブジェクト指向プログラミング並に結構プログラムの考え方自体を変えないといけない部分がある言語なので。このあたりが学習難易度を上げている要因だと思います。Rustには思想がくっついています。なので、基本的に「嫌なニオイ」がしたら、自分が間違えていて思想に則った方法があるはずだという考えで進めると割とRust君は怒ってきません。優しい子になってくれます。 https://zenn.dev/link/comments/3c25b540343184 このように、Rustにおいては設計思想が重要というのは、他の方が仰っているとおりかなと思います。 なんでもかんでもRustを使えばいい、という思考から、適切な選択をできるようになった頃にRustは強力な味方になります。 Rust学習、もし続けられるのであれば応援しています。 返信を追加Nakamura2025/04/10に更新 同じ機能をTypeScriptやPythonで実装していたら、おそらく半分の時間で2倍の機能を実装できていただろう。確かにメモリ安全性は低下するかもしれないが、開発者のメンタルヘルスは保たれていただろう。 私は半分の時間で2倍の機能どころじゃなかったです。使い慣れたDenoならライブラリ不要で1時間でできるようなことに何日も費やしました。そんなんだからRustを諦めるべきかどうかで1ヶ月くらい悩みました・・Rustにしかないライブラリを使うために何とか続けられた感じです。 もしくはGoを選ぶべきだったかもしれない。Goはシンプルな構文、高速なコンパイル、実用的な並行処理モデル、そして何より「合理的に動く」という強みがある。Rustのような完璧なメモリ安全性はないが、ガベージコレクションの予測可能性と良好なパフォーマンスが得られる この気持ちめっちゃわかります!Rustやりながら詰まったらついついGoについて調べちゃうんですよね😄 返信を追加horz2025/04/10c++で組んでいたとき、たった一文字抜けていただけで鬼のようなコンパイルエラーやらリンクエラーが出て途方に暮れたことを思い出した。 返信を追加みんてぃ2025/04/162.1 に関してはすでにご覧になっていると思いますが、 https://zenn.dev/nazo6/articles/rust-kirai-kansou こちらの記事のように trait を実装することで緩和される可能性がある気がしました。 個人的にはダッグタイピングを行うような感覚で DI していくと良い気がしています。 素人意見で恐縮です。 返信を追加nium_dev2025/04/20Rust入門したところなのでドキドキしながら読みました。 私はまだそこまで難しいことにぶつかっていないほどの入門者なので、今後覚悟して進んでいこうと思います…。 返信を追加
todesking2025/04/08 「メモリ安全な言語」なのに、静的文字列を生成するために意図的にメモリリークを起こすBox::leakを使わざるを得ない皮肉。これが「直感的」で「安全」なコードの書き方なのか? BuildInfoのメンバの型を &'static strではなくStringにすればリークさせる必要は一切ないと思うのですが、あえて &'static str を使わなければいけない事情があったのでしょうか……? あと記事の趣旨とはそれますが、このBuildInfoという構造体はなんの用途で使っているのでしょうか? ビルド時間としてプログラム実行時の現在時刻が指定されているのはおかしい気がする。 パラッコリー2025/04/09 BuildInfoのメンバの型を &'static strではなくStringにすればリークさせる必要は一切ないと思うのですが、あえて &'static str を使わなければいけない事情があったのでしょうか……? おっしゃる通りです!これは完全に私の設計ミスでした。「静的文字列参照を返す関数」という固定観念にとらわれていました。ご指摘いただき、ありがとうございます。 あと記事の趣旨とはそれますが、このBuildInfoという構造体はなんの用途で使っているのでしょうか? ビルド時間としてプログラム実行時の現在時刻が指定されているのはおかしい気がする。 こちらも本来ならbuild_dateはビルド時の時刻を示すべきで、実行時の時刻ではおかしいですね。正しい実装であれば、ビルド時の情報を埋め込むマクロを使うべきでした。 pub fn build_info() -> BuildInfo { BuildInfo { version: VERSION.to_string(), // コンパイル時の時刻を埋め込む build_date: env!("BUILD_DATE").to_string(), commit_hash: option_env!("GIT_HASH").map(|s| s.to_string()), rust_version: rustc_version_runtime::version().to_string(), } } この構造体は主にログ出力やアプリのAbout画面、デバッグ情報表示に使っていました。実際にはビルドスクリプトでコンパイル時の環境変数を設定し、それを埋め込むのが正しいアプローチですね。私の設計と理解の問題だったと認めざるを得ません。 返信を追加
パラッコリー2025/04/09 BuildInfoのメンバの型を &'static strではなくStringにすればリークさせる必要は一切ないと思うのですが、あえて &'static str を使わなければいけない事情があったのでしょうか……? おっしゃる通りです!これは完全に私の設計ミスでした。「静的文字列参照を返す関数」という固定観念にとらわれていました。ご指摘いただき、ありがとうございます。 あと記事の趣旨とはそれますが、このBuildInfoという構造体はなんの用途で使っているのでしょうか? ビルド時間としてプログラム実行時の現在時刻が指定されているのはおかしい気がする。 こちらも本来ならbuild_dateはビルド時の時刻を示すべきで、実行時の時刻ではおかしいですね。正しい実装であれば、ビルド時の情報を埋め込むマクロを使うべきでした。 pub fn build_info() -> BuildInfo { BuildInfo { version: VERSION.to_string(), // コンパイル時の時刻を埋め込む build_date: env!("BUILD_DATE").to_string(), commit_hash: option_env!("GIT_HASH").map(|s| s.to_string()), rust_version: rustc_version_runtime::version().to_string(), } } この構造体は主にログ出力やアプリのAbout画面、デバッグ情報表示に使っていました。実際にはビルドスクリプトでコンパイル時の環境変数を設定し、それを埋め込むのが正しいアプローチですね。私の設計と理解の問題だったと認めざるを得ません。
chicuwa2025/04/08Rustを趣味のプログラムで使っているRust信者です。 ビルド時間の部分は特にすごくわかりますね。本当に遅いし、依存関係周りで問題が起こると本当に分かりづらい。本当に...。ただ、Rustは実行時の速度とビルドの速度を天秤にかけて実行時の速度に重きをおいているという点は補足しておきたいです。プログラマーにではなく、ユーザーに優しい言語です。 あと、デバッグが大変なのもわかります。wasmはまだ触ってないのでprintデバックすら厳しいのは知らなかったですが、スレッド内のパニックとかも要因がなかなか掴めなくてしんどいです。ここはネットで検索する限りRustの弱点として言われてることが(観測範囲では割と)多く、解決策らしい解決策も少なくともWindows環境では知らないです。Linux、macだと割とあるらしいですが。 所有権やライフタイムも初心者が苦しむところですね。というか自分も'a、'bとかを指定するときはまだ苦しんでいます。ただ、基本的にBox::leakを使わないといけない時点で設計のほうが間違えてるのでは?と自分なら考えますね。オブジェクト指向プログラミング並に結構プログラムの考え方自体を変えないといけない部分がある言語なので。このあたりが学習難易度を上げている要因だと思います。Rustには思想がくっついています。なので、基本的に「嫌なニオイ」がしたら、自分が間違えていて思想に則った方法があるはずだという考えで進めると割とRust君は怒ってきません。優しい子になってくれます。 Rustのエコシステムは本来、Rust内で閉じてさえいれば問題が起こったことがないので、「Rustで完結していれば」本当に優秀なエコシステムだと思っています。ただ、C/C++だったり、wasmだったり外部の連携をしようとすると、しんどいことはあります。ただこれはある程度仕方ない部分が大きい気がしています。外部の側が悪いこともありますし、そもそも全く別の言語同士をつなげること自体に少し無理がある気がするからです。 マルチプラットフォーム対応はしたことがないのでわかりませんが、Rustライブラリのexampleを見たときに#[cfg]の嵐で面食らったことはあります。ただ、トレイトかEnumを使って抽象化すればある程度#[cfg]の嵐は防げるのかな...?ここは本当に詳しくないのでなんとも言えませんが。 エラーメッセージは好み分かれますね。たまに長すぎて読む気の起きないやつはあります。特にトレイト系は。ただ、基本は必要十分だと思います。VSCode使ってると、ざっくりとしたエラーだけも確認できるので、必要だったら見るという感覚でした。 それぞれの章に言いたいことがあったから全部書いてたらめっちゃ長くなっちゃいましたが、言いたいことはこんな感じです!改めてRustについて考えさせてくれる良い記事をありがとうございます!やっぱり、批判なしに良い言語コミュニティは作れないので。 返信を追加
prbt2025/04/09ぱっと見クローンで躓いているようなので所有権がまだ分かっていないのかなと思いました。 メモリ安全性等のメリットはあくまでC++比較ということでその他のGCを使っている言語からすれば複雑なことをひたすらやっているように感じます。仕方のないことです。 正直個人で使うメリットは薄いです。大規模開発で保守性が活きてくるようなイメージです 返信を追加
Toru32025/04/09に更新[profile.release] lto = true codegen-units = 1 opt-level = 3 panic = "abort" strip = true この設定ですが意味を理解して書かれてます? LTOはリンク時最適化なので有効にすればビルドは遅くなります。 codegen-units は最大何並列でコード生成するかなので1にすれば当然ビルドは遅くなります。 返信を追加
すずねーう2025/04/09 コントラストとして、Node.jsのエコシステムでは: # 通常はこれだけでOK npm install npm start 過去に node-gyp のエラーで2日無駄にしたので、Rust エコシステムと npm だけで解決してる方が良くないすか?と思ってしまう 返信を追加
タコ2025/04/09過激な言葉で批判している割に、所有権やライフタイムを全く理解しておらず、出鱈目で無知を晒しているだけの面白みに欠ける記事だと思いました。 同じ機能をTypeScriptやPythonで実装していたら、おそらく半分の時間で2倍の機能を実装できていただろう。 TypeScriptやPythonで済むのであれば、Rustを使う必要はないでしょうね。でも、TypeScriptやPythonしか書けない人は、それらのインタプリタを修正することすらできないでしょうね。 CPythonのコードを読んだことありますか?自分でメモリの確保と解放をしなければならず、少し注意が散漫になったら不正なコードを書いてしまいそうになります。Rustの所有権システムは、CやC++で人間が注意しなければならなかったことをコンパイラーが代替するものです。 返信を追加
makoto-developer2025/04/09慣れるとRustのエラーメッセージは親切にしかみえなくなる。 たしかに学習曲線は中級者まで角度が高めかもですね。私も最初は本を読みながらやってたけどバージョンを同じにしてもビルドで動かず結構詰みました。手前味噌ですがRustをやる前にC++を学んだほうが良いかもしれないです。私は遠回りしてC++から入ってRustを学びました。なぜRustが誕生しなければならなかったのかを体験することでありがたみを理解できました。ここはやはりメモリ管理の地獄を渡り歩く経験をしておくとRustの所有権がなんて素晴らしい仕組みなんだとありがたみを知ることで恩恵を受けられます。何のアドバイスにもならないですがRustはただ速いとか安全とかだけで踏み込むとそこはジャングルですぐ帰りたくなるかもしれないそんな気持ちは確かに理解できます。 なんかおすすめの本とかないかな。結局私は公式のチュートリアルとRustのソースコードを読んで乗り切ったのでなんともですが良いのを見つけたらここに書きますね。 返信を追加
dalance2025/04/09に更新#[cfg] については記事中にあるように細かい単位で切り替えようと思うと大変なので、ファイル単位くらいにすると楽かもしれません。 // system.rs // 実装を切り替えつつ、各実装のシンボルをエクスポートする #[cfg(target_os = "windows")] mod system_windows; #[cfg(target_os = "linux")] mod system_linux; #[cfg(target_os = "windows")] pub use system_windows::*; #[cfg(target_os = "linux")] pub use system_linux::*; // system_windows.rs pub struct System {} // system_linux.rs pub struct System {} // main.rs // systemからエクスポートされたものを使えば各プラットフォーム毎の実装になっている let system = system::System {}; WASMは大変ですよね…。Rustのエコシステムの中でも結構ユーザ体験の悪い部類な気はしています。 「CLIやサーバサイド向けに書いたコードがこの程度の手間でブラウザでも動く」という観点では結構ありがたいのですが、 ブラウザをメインターゲットにするならやはりJS/TSが楽だろうと思います。 返信を追加
r-sugi6ヶ月前に更新色んなコメントがありますが、個人的に面白かったです! 学習コスト高いのに、(Web系の)フリーランス案件数が少ないからRustやることはなさそう。 リントとかのツール系ではお世話になっています。 返信を追加
まぬけのさひろ2025/04/10に更新いろいろありますが誰も指摘してない点を1つ メモリリークはRust的には「安全」です https://doc.rust-jp.rs/rust-nomicon-ja/meet-safe-and-unsafe.html https://doc.rust-jp.rs/rust-nomicon-ja/leaking.html 返信を追加
kkent0303152025/04/10に更新Rustについては、所有権の概念というより、それに基づいたデザインを考えることが最も難しいとされる部分なのではないかと思います。 もっとも、BuildInfoについては、Rustらしい書き方をすることで解決すると考えます。 この場合、すべてのフィールドはビルド時のメタデータを含むので、ランタイムで処理を行う必要はありません。 すべてのフィールドを静的ライフタイムとして定義するのがパフォーマンスの観点からしても適切な実装です。 Cでは、明示的に定義実装しない限り、すべての文字列は基本的に静的なライフタイムを持ちます。このRustプログラムでは、意味論的には等価のことを行っています。 [build-dependencies]はビルド時のみに必要な依存関係を解決します。これにより、製品バイナリに不要なコードが含まれることを回避します。 Cargo.toml [package] name = "buildinfo" version = "0.1.0" edition = "2024" [dependencies] [build-dependencies] chrono = "0.4.40" rustc_version = "0.4.1" BuildInfoはライフタイムを明示的に指定することでもっと安い実装に置換できます。Stringは基本的にヒープを使うので、ビルド時メタデータに使用するには高価すぎます。 main.rs #[derive(Debug)] pub struct BuildInfo<'a> { version: &'a str, build_date: &'a str, commit_hash: Option<&'a str>, rustc_version: &'a str, } さらに、build_info関数はconst関数にすることができます。これにより、関数はランタイムではなくビルド時に評価されます。 main.rs pub const fn build_info<'a>() -> BuildInfo<'a> { BuildInfo { version: env!("CARGO_PKG_VERSION"), build_date: env!("BUILD_DATE"), commit_hash: option_env!("GIT_HASH"), rustc_version: env!("RUSTC_VERSION"), } } env!マクロを使用するこれらの環境変数はビルド時に定義されている必要があるので、build.rsを用意します。 これはビルドスクリプトと呼ばれるもので、cargo buildを実行したときに実行されます。ここで、必要な環境変数をセットします。 このスクリプトはビルド環境でのみ実行され、ランタイムの実行バイナリに影響を与えません。 build.rs fn main() { let build_date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); println!("cargo:rustc-env=BUILD_DATE={build_date}",); println!( "cargo:rustc-env=RUSTC_VERSION={}", rustc_version::version().expect("Unable to retrieve rustc version") ); } この方法でビルドしたバイナリは非常にクリーンで、全体を通してパニックフリーです。 main.rs fn main() { let info = build_info(); println!("Build info: {info:?}"); } オブジェクト指向プログラミング並に結構プログラムの考え方自体を変えないといけない部分がある言語なので。このあたりが学習難易度を上げている要因だと思います。Rustには思想がくっついています。なので、基本的に「嫌なニオイ」がしたら、自分が間違えていて思想に則った方法があるはずだという考えで進めると割とRust君は怒ってきません。優しい子になってくれます。 https://zenn.dev/link/comments/3c25b540343184 このように、Rustにおいては設計思想が重要というのは、他の方が仰っているとおりかなと思います。 なんでもかんでもRustを使えばいい、という思考から、適切な選択をできるようになった頃にRustは強力な味方になります。 Rust学習、もし続けられるのであれば応援しています。 返信を追加
Nakamura2025/04/10に更新 同じ機能をTypeScriptやPythonで実装していたら、おそらく半分の時間で2倍の機能を実装できていただろう。確かにメモリ安全性は低下するかもしれないが、開発者のメンタルヘルスは保たれていただろう。 私は半分の時間で2倍の機能どころじゃなかったです。使い慣れたDenoならライブラリ不要で1時間でできるようなことに何日も費やしました。そんなんだからRustを諦めるべきかどうかで1ヶ月くらい悩みました・・Rustにしかないライブラリを使うために何とか続けられた感じです。 もしくはGoを選ぶべきだったかもしれない。Goはシンプルな構文、高速なコンパイル、実用的な並行処理モデル、そして何より「合理的に動く」という強みがある。Rustのような完璧なメモリ安全性はないが、ガベージコレクションの予測可能性と良好なパフォーマンスが得られる この気持ちめっちゃわかります!Rustやりながら詰まったらついついGoについて調べちゃうんですよね😄 返信を追加
みんてぃ2025/04/162.1 に関してはすでにご覧になっていると思いますが、 https://zenn.dev/nazo6/articles/rust-kirai-kansou こちらの記事のように trait を実装することで緩和される可能性がある気がしました。 個人的にはダッグタイピングを行うような感覚で DI していくと良い気がしています。 素人意見で恐縮です。 返信を追加
nium_dev2025/04/20Rust入門したところなのでドキドキしながら読みました。 私はまだそこまで難しいことにぶつかっていないほどの入門者なので、今後覚悟して進んでいこうと思います…。 返信を追加
Discussion
BuildInfoのメンバの型を&'static strではなくStringにすればリークさせる必要は一切ないと思うのですが、あえて&'static strを使わなければいけない事情があったのでしょうか……?あと記事の趣旨とはそれますが、この
BuildInfoという構造体はなんの用途で使っているのでしょうか?ビルド時間としてプログラム実行時の現在時刻が指定されているのはおかしい気がする。
おっしゃる通りです!これは完全に私の設計ミスでした。「静的文字列参照を返す関数」という固定観念にとらわれていました。ご指摘いただき、ありがとうございます。
こちらも本来なら
build_dateはビルド時の時刻を示すべきで、実行時の時刻ではおかしいですね。正しい実装であれば、ビルド時の情報を埋め込むマクロを使うべきでした。この構造体は主にログ出力やアプリのAbout画面、デバッグ情報表示に使っていました。実際にはビルドスクリプトでコンパイル時の環境変数を設定し、それを埋め込むのが正しいアプローチですね。私の設計と理解の問題だったと認めざるを得ません。
Rustを趣味のプログラムで使っているRust信者です。
ビルド時間の部分は特にすごくわかりますね。本当に遅いし、依存関係周りで問題が起こると本当に分かりづらい。本当に...。ただ、Rustは実行時の速度とビルドの速度を天秤にかけて実行時の速度に重きをおいているという点は補足しておきたいです。プログラマーにではなく、ユーザーに優しい言語です。
あと、デバッグが大変なのもわかります。wasmはまだ触ってないのでprintデバックすら厳しいのは知らなかったですが、スレッド内のパニックとかも要因がなかなか掴めなくてしんどいです。ここはネットで検索する限りRustの弱点として言われてることが(観測範囲では割と)多く、解決策らしい解決策も少なくともWindows環境では知らないです。Linux、macだと割とあるらしいですが。
所有権やライフタイムも初心者が苦しむところですね。というか自分も
'a、'bとかを指定するときはまだ苦しんでいます。ただ、基本的にBox::leakを使わないといけない時点で設計のほうが間違えてるのでは?と自分なら考えますね。オブジェクト指向プログラミング並に結構プログラムの考え方自体を変えないといけない部分がある言語なので。このあたりが学習難易度を上げている要因だと思います。Rustには思想がくっついています。なので、基本的に「嫌なニオイ」がしたら、自分が間違えていて思想に則った方法があるはずだという考えで進めると割とRust君は怒ってきません。優しい子になってくれます。Rustのエコシステムは本来、Rust内で閉じてさえいれば問題が起こったことがないので、「Rustで完結していれば」本当に優秀なエコシステムだと思っています。ただ、C/C++だったり、wasmだったり外部の連携をしようとすると、しんどいことはあります。ただこれはある程度仕方ない部分が大きい気がしています。外部の側が悪いこともありますし、そもそも全く別の言語同士をつなげること自体に少し無理がある気がするからです。
マルチプラットフォーム対応はしたことがないのでわかりませんが、Rustライブラリのexampleを見たときに
#[cfg]の嵐で面食らったことはあります。ただ、トレイトかEnumを使って抽象化すればある程度#[cfg]の嵐は防げるのかな...?ここは本当に詳しくないのでなんとも言えませんが。エラーメッセージは好み分かれますね。たまに長すぎて読む気の起きないやつはあります。特にトレイト系は。ただ、基本は必要十分だと思います。VSCode使ってると、ざっくりとしたエラーだけも確認できるので、必要だったら見るという感覚でした。
それぞれの章に言いたいことがあったから全部書いてたらめっちゃ長くなっちゃいましたが、言いたいことはこんな感じです!改めてRustについて考えさせてくれる良い記事をありがとうございます!やっぱり、批判なしに良い言語コミュニティは作れないので。
ぱっと見クローンで躓いているようなので所有権がまだ分かっていないのかなと思いました。
メモリ安全性等のメリットはあくまでC++比較ということでその他のGCを使っている言語からすれば複雑なことをひたすらやっているように感じます。仕方のないことです。
正直個人で使うメリットは薄いです。大規模開発で保守性が活きてくるようなイメージです
この設定ですが意味を理解して書かれてます?
LTOはリンク時最適化なので有効にすればビルドは遅くなります。
codegen-unitsは最大何並列でコード生成するかなので1にすれば当然ビルドは遅くなります。過去に node-gyp のエラーで2日無駄にしたので、Rust エコシステムと npm だけで解決してる方が良くないすか?と思ってしまう
過激な言葉で批判している割に、所有権やライフタイムを全く理解しておらず、出鱈目で無知を晒しているだけの面白みに欠ける記事だと思いました。
TypeScriptやPythonで済むのであれば、Rustを使う必要はないでしょうね。でも、TypeScriptやPythonしか書けない人は、それらのインタプリタを修正することすらできないでしょうね。
CPythonのコードを読んだことありますか?自分でメモリの確保と解放をしなければならず、少し注意が散漫になったら不正なコードを書いてしまいそうになります。Rustの所有権システムは、CやC++で人間が注意しなければならなかったことをコンパイラーが代替するものです。
慣れるとRustのエラーメッセージは親切にしかみえなくなる。
たしかに学習曲線は中級者まで角度が高めかもですね。私も最初は本を読みながらやってたけどバージョンを同じにしてもビルドで動かず結構詰みました。手前味噌ですがRustをやる前にC++を学んだほうが良いかもしれないです。私は遠回りしてC++から入ってRustを学びました。なぜRustが誕生しなければならなかったのかを体験することでありがたみを理解できました。ここはやはりメモリ管理の地獄を渡り歩く経験をしておくとRustの所有権がなんて素晴らしい仕組みなんだとありがたみを知ることで恩恵を受けられます。何のアドバイスにもならないですがRustはただ速いとか安全とかだけで踏み込むとそこはジャングルですぐ帰りたくなるかもしれないそんな気持ちは確かに理解できます。
なんかおすすめの本とかないかな。結局私は公式のチュートリアルとRustのソースコードを読んで乗り切ったのでなんともですが良いのを見つけたらここに書きますね。
うーん、Cを学びましょう
Rustで書かれたDenoは実質RustなのでDeno使おう!(?)
#[cfg]については記事中にあるように細かい単位で切り替えようと思うと大変なので、ファイル単位くらいにすると楽かもしれません。WASMは大変ですよね…。Rustのエコシステムの中でも結構ユーザ体験の悪い部類な気はしています。
「CLIやサーバサイド向けに書いたコードがこの程度の手間でブラウザでも動く」という観点では結構ありがたいのですが、
ブラウザをメインターゲットにするならやはりJS/TSが楽だろうと思います。
色んなコメントがありますが、個人的に面白かったです!
学習コスト高いのに、(Web系の)フリーランス案件数が少ないからRustやることはなさそう。
リントとかのツール系ではお世話になっています。
いろいろありますが誰も指摘してない点を1つ
メモリリークはRust的には「安全」です
Rustについては、所有権の概念というより、それに基づいたデザインを考えることが最も難しいとされる部分なのではないかと思います。
もっとも、
BuildInfoについては、Rustらしい書き方をすることで解決すると考えます。この場合、すべてのフィールドはビルド時のメタデータを含むので、ランタイムで処理を行う必要はありません。
すべてのフィールドを静的ライフタイムとして定義するのがパフォーマンスの観点からしても適切な実装です。
Cでは、明示的に定義実装しない限り、すべての文字列は基本的に静的なライフタイムを持ちます。このRustプログラムでは、意味論的には等価のことを行っています。
[build-dependencies]はビルド時のみに必要な依存関係を解決します。これにより、製品バイナリに不要なコードが含まれることを回避します。BuildInfoはライフタイムを明示的に指定することでもっと安い実装に置換できます。Stringは基本的にヒープを使うので、ビルド時メタデータに使用するには高価すぎます。さらに、
build_info関数はconst関数にすることができます。これにより、関数はランタイムではなくビルド時に評価されます。env!マクロを使用するこれらの環境変数はビルド時に定義されている必要があるので、build.rsを用意します。これはビルドスクリプトと呼ばれるもので、
cargo buildを実行したときに実行されます。ここで、必要な環境変数をセットします。このスクリプトはビルド環境でのみ実行され、ランタイムの実行バイナリに影響を与えません。
この方法でビルドしたバイナリは非常にクリーンで、全体を通してパニックフリーです。
このように、Rustにおいては設計思想が重要というのは、他の方が仰っているとおりかなと思います。
なんでもかんでもRustを使えばいい、という思考から、適切な選択をできるようになった頃にRustは強力な味方になります。
Rust学習、もし続けられるのであれば応援しています。
私は半分の時間で2倍の機能どころじゃなかったです。使い慣れたDenoならライブラリ不要で1時間でできるようなことに何日も費やしました。そんなんだからRustを諦めるべきかどうかで1ヶ月くらい悩みました・・Rustにしかないライブラリを使うために何とか続けられた感じです。
この気持ちめっちゃわかります!Rustやりながら詰まったらついついGoについて調べちゃうんですよね😄
c++で組んでいたとき、たった一文字抜けていただけで鬼のようなコンパイルエラーやらリンクエラーが出て途方に暮れたことを思い出した。
2.1 に関してはすでにご覧になっていると思いますが、 こちらの記事のように trait を実装することで緩和される可能性がある気がしました。
個人的にはダッグタイピングを行うような感覚で DI していくと良い気がしています。
素人意見で恐縮です。
Rust入門したところなのでドキドキしながら読みました。
私はまだそこまで難しいことにぶつかっていないほどの入門者なので、今後覚悟して進んでいこうと思います…。