3 ポイント 投稿者 GN⁺ 2023-10-26 | 1件のコメント | WhatsAppで共有
  • WebAssemblyに集中するためJavaScriptからRustへ移行した後、3年間でWick、本番環境へのデプロイ、ebook、約100個のcrates.ioパッケージを作りながら、Rustの実際の価値を評価した
  • borrow checker、豊富な型システム、関数型パターン、nullがないことにより、多くのエラーをコンパイル段階で防ぎ、少ないテストで大規模なコードベースを維持できるようになった
  • ClippyとCargo workspaceは強力だが、グローバルなlint設定やworkspaceの配布のようなツール・エコシステムの隙間が運用コストにつながる
  • async、リファクタリング、generic・lifetime・trait constraintの管理は、JavaScriptやGoよりも摩擦が大きい領域として残っている
  • Rustは堅牢で多用途だが、採用、学習、高速な反復、問題追跡のコストが大きいため、スコープが明確な場合や初期コストを負担できる場合により適している

WebAssemblyがRust選択を後押しした

  • 数年前、WebAssemblyに100%集中するため既存の仕事を手放し、当時RustはWebAssemblyへのコンパイル対応が最も優れていた
  • 機能豊富なWebAssemblyランタイムもRustベースだったため、選択肢の中でRustが最も現実的な選択肢だった
  • その後、WebAssemblyを中核的なモジュールシステムとして使うアプリケーションフレームワーク兼ランタイムであるWickを作った
  • 3年間で複数の本番環境デプロイebook、crates.ioへの約100個のパッケージ公開を通じて、Rustの経験が蓄積された

少ないテストでより多くのコードを維持

  • Rustでは一般的な言語と同じようにテストを書いているうちに、コンパイルさえ通れば失敗しようがないテストを書いていることに気づいた
  • unsafe {}ブロックや.unwrap()のようなpanicを起こしやすいメソッドを避ければ、多くの問題は基本的に回避できる
  • borrow checker、豊富な型システム、関数型パターンとライブラリ、null値が存在しないことが、テストにかかる労力を減らす
  • Wickプロジェクトの70,000行以上のコードを、他の言語で必要だったであろうテストよりはるかに少ないテストで維持した
  • テストが必要な場合は、Rustの統合テストハーネスのおかげでコードのそばに簡単に追加できる

Rustは他の言語でのコーディング習慣も変えた

  • Rustコンパイラは、他の言語では正常だと思っていたコードにも絶えず不満を示し、その過程でコーディング習慣が変わった
  • 今では他の言語でも、コード行の順序が不自然だったり戻り値を確認しなかったりすると居心地の悪さを感じる
  • ランタイムエラーに遭遇したときも、以前よりはるかに強い拒否感を覚えるようになった
  • Rustの厳格さは不便だが、コンパイラに守られる経験に慣れると他の言語に戻るのは難しい

Clippyはリンター以上に役立つ

  • ClippyはRustのリンターだが、単なる検査ツールというより、代替コードを提案してくれる親切な補助ツールに近い
  • Rust標準ライブラリは非常に大きく、機能が多くの型・trait・マクロ・関数に分散しているため、必要なAPIを見つけにくい
  • 複数のルールは、標準ライブラリのメソッドや型でよりよく置き換えられる一般的なパターンを見つけ出す
  • 数百のルールが性能、可読性、不要な間接化を扱い、可能な場合は代替コードも提供する
  • プロジェクト全体のlint設定はCargoのIssueによって可能になりそうだったが、それまではWickでは数十個のcrateのinline lint設定をスクリプトで自動更新する必要があった

エコシステムには受け入れるべき隙間がある

  • グローバルなClippy設定の問題は、Rustのツールやライブラリでしばしば遭遇するエコシステムの隙間の一例だ
  • 関連Issueは現在はクローズされているが、何年もオープンのままで、解決まで時間がかかった
  • Rustは長い間「最も愛されている言語」に選ばれるほど新規ユーザーを引きつけているが、その流れがライブラリやツールの劇的な改善にそのままつながっているわけではない
  • 特定のユースケースに対応する一回限りのforkが生まれることが多く、WickでもPRを出そうとしたが似た状況に遭遇した
  • 考えられる理由として、安定したAPIを維持するプレッシャーと、細分化された型システムがある
    • ライブラリの所有者にとっては、小さな変更でもメジャーバージョン変更につながり得るため受け入れにくい
    • すべての人の要求を満たすRustコードを書く負担も大きい

Cargo、crates.io、workspace配布の摩擦

  • Wickのリポジトリ構造は人気プロジェクトを参考にして作り、最初は妥当に見えたが、配布段階で問題が明らかになった
  • Cargoでモジュールサイズのcrateをビルド・テスト・利用するのは簡単だが、crates.ioへの配布は別問題だ
  • crates.ioでは、参照するすべてのcrateが個別に配布されていなければパッケージをpublishできない
    • ローカルファイルシステムにしか存在しないパッケージに依存したcrateを配布できないようにするのは妥当だ
    • しかし、大きなプロジェクトを小さな内部モジュールに分割する自然な構造では、親crateの中にだけ存在するsub-crateを含めてpublishすることができない
  • ローカルのdev dependencyがあるcrateでも、Cargo.tomlversionを含めなければpublishできる、という訂正があった
  • Cargo workspaceのサポート自体は素晴らしく、大規模プロジェクトの管理体験はほとんどの言語より優れている
  • しかしworkspaceは配布問題を解決せず、設定方法がいくつかあっても簡単に配布できる「正解」は見つけにくい
  • cargo workspace publish関連のユーティリティcrateが多数存在すること自体が問題を示している
  • Wickをpublishするときは、手作業の反復作業と部分的にしか動かないツールを組み合わせる必要があり、1時間以上かかることがよくある

asyncは最大の摩擦の一つ

  • Rustのasyncは、言語が最初に作られた後で追加された機能のように感じられ、実際の使用でも後付けの機能のように頻繁に妨げになる
  • エラーを理解して解決するのが難しく、解決策を探すときも複数のランタイムとそれぞれのasync方式を基準に絞り込む必要がある
  • あるasyncライブラリは特定のasyncランタイムの外では使えない可能性がある
  • JavaScriptを20年使い、Goの経験もある立場からすると、Rust asyncは最大の挫折と摩擦の源だ
  • 克服不可能な問題ではないが、asyncの問題がいつでも現れ得ることを常に想定しておく必要がある
  • 他の言語ではasyncがほとんど見えないほど自然に動作する

リファクタリングはつらい作業になり得る

  • Rustの豊富な型システムは長所であると同時に短所でもある
  • Rustの型で考えることは良いが、Rustの型を管理することは悪夢になり得る
  • データと関数シグネチャにはgeneric type、generic lifetime、trait constraintが入り得る
  • constraint自体もさらにgeneric typeやlifetimeを持つことがあり、実際のコードより型constraintのほうが多い場合もある
  • すべてのimplごとにgenericを定義する必要があるため、最初に書くときも面倒で、リファクタリング時には小さな変更が連鎖的な修正に膨らみ得る
  • 同じconstraintやgenericの一覧を複数箇所で繰り返す必要があるとき、それをaliasしたり中央定義として参照したりする言語・ツールレベルの方法がなく、重複の負担が残る

最終判断: 強力だがコストが大きい

  • Rustはシステムレベルのコード、CLIアプリ、Webサーバー、Webクライアントを同じ言語で書けるほど多用途だ
  • WebAssemblyを使えば、同じバイナリでLLMをブラウザとコマンドラインで実行できる
  • Rustプログラムは非常に堅牢になり得るし、Rustが防いでくれる問題を実感すると他の言語に戻るのは難しい
  • Goに一時的に戻ったとき、開発速度は再び魅力的だったが、ランタイムpanicを経験した後でその利点は揺らいだ
  • Rustには明確な欠点がある
    • 採用が難しい
    • 学習が遅い
    • 高速な反復には硬直的すぎる
    • 特にasyncコードではメモリと性能の問題を追跡しにくい
    • すべてのライブラリが安全なコードに十分適しているわけではない
    • 開発ツールにはさらに改善の余地が大きい
  • 小さなチームで驚くべきことを成し遂げたが、大きな障害もあり、Rustのほうが適していた技術的理由もあったため、WickにとってRustに価値があったかを判断するにはまだ早いと考えている
  • 素早く反復する必要があるなら、Rustは適していない可能性が高い
  • スコープが分かっている場合や、より大きな初期コストを負担できる場合は、Rustを真剣に検討する価値がある
  • WebAssemblyの観点が月を追うごとに強まり、一度書いた堅牢なソフトウェアを複数の場所で再利用できる可能性がより現実に近づいている

1件のコメント

 
GN⁺ 2023-10-26
Hacker Newsの意見
  • Rustはかなり使ってきたが、数年たってもいまだに生産性が低いと感じる
    最近はZigをよく使っていて、書きたいコードだけに集中でき、どのツールやライブラリを使うべきか悩まなくてよいので、10倍くらい生産的に感じる
    Rustがメモリ安全性を提供してくれて、それが重要だというのは分かるが、使い勝手が本当に悪い。Rustを書くたびに制約されている感じがして、いつもライブラリを探したり作業方法を検索したりしなければならず、ただ「コードをタイプする」ことができない
    型システムも制御不能なほど膨らむことがあり、ある構造体で実際にどのメソッドを呼べるのか分かりにくいことも多い。Rustは優れたツールで、多くの問題を解決するが、よい汎用言語だとは思わない

    • その感覚が誰にとっても共通というわけではない。シェルスクリプトより少しでも複雑な作業ならRustを使うし、自分のウィンドウマネージャーもRustプログラムで制御している
      Pythonをほぼ20年使ってきた立場から言っても、今はRustでもPythonと同じくらい速く作業できる
    • 同意する。現時点ではRustよりCとC++のほうがはるかに生産的だと感じる
      Rustは自分の基準では、適切なバランスを完全に外しているように思う。高水準アプリケーションを書くには低水準の細部にうるさすぎ、組み込みやOSを書くには複雑すぎる
      前者ならC++、Java、Haskell、OCaml、あるいはGoにCを少し混ぜる選択をするし、後者ならマクロアセンブリのように使うCのほうがずっと適している
      Graydon Hoareの当初の構想、つまり線形型、ガベージコレクション、スタック割り当て、グリーンスレッド、CPSを備えた
      OCaml/SML
      寄りのもののほうが、ずっとよい言語になっていたのではないかという気が今でもする
    • 自分の経験は正反対だ。組み込みシステムでRustを使うことで、自信も速度も大きく向上した
      Cでは小さなミス一つが未定義動作や厄介ごとにつながることが多いが、Rustにはそれがないので、完全にゲームチェンジャーだった
    • どんな種類のコードを書いているのか気になる。かなり低水準なのか、それともかなり高水準なのか?
      Zigではライブラリを探したり、どうすべきか調べたりする必要がない、という意味なのか気になる
    • 全体的な感覚には同意するが、言葉にするのが難しい
      Rustは各ビットやバイトがどこへ行くのか、どのスレッドで使われるのか、どのような変更方法で扱われるのかを事前に決めるよう強いる。パーサーやマイクロコントローラのレベルでなければ、この過程は退屈に感じる
      まず何かを動くようにしてから最適なAPI構造を決めるやり方が好きだが、Rustはそのプロセスと衝突する
      Rustの型システムのほうが強力ではあるものの、Swiftでも性能の90%は得られ、ずっと自然に流れる
  • crates.ioに名前空間がない点が最大の批判点かもしれない
    誰でもグローバルで一般的なパッケージ名を先取りでき、crates.ioリポジトリを避けない限り、ほとんどの人はそれを受け入れざるを得ない。しかも、そうして先取りされた一般名のパッケージの一部は、実際に使ううえで最良のパッケージではない
    Java式の逆順DNS表記が冗長で面倒だったことへの反動だったのかもしれないが、GitHubのようにユーザー/グループの名前空間をパッケージ名の前に付ける方式がよい中間点だったように思う

    • crates.ioで名前の先取り者を探す分析をしたところ、最上位の先取り者は1週間ずっと約30秒に1つのペースでクレートを作っている計算になった
      その分析をcrates.ioチームに送り、自動化禁止ポリシーがある点も指摘したが、名前を先取りしたという十分な証拠ではない、という返答だった
      crates.ioの問題は、明確なポリシーがあっても執行しないことだ。だから短く覚えやすいクレート名はすでにすべて取られていて、取り戻す方法もない
    • Java式の逆順DNSへの反動というより、当時の多くのパッケージマネージャーが作った慣例に従ったものに近いように見える
      NPM、PyPI、RubyGems、ElixirのHex、HaskellのCabalなど、Rustが登場した2014〜2015年ごろ、Java以外のパッケージマネージャーで単一のグローバル名前空間ではなかった例はあまり思い浮かばない
      一部はその後これを直そうとしたが、当時は単にパッケージマネージャーがそう動く時代だった
    • MavenとJavaは、依存関係管理がうまく機能しているという点で十分に評価されていない
      その後に出てきた他の言語の、より出来の悪い依存関係管理システムは、先行事例からほとんど学ばなかった
    • パッケージにURLを使う方式はかなり理にかなっている。Goエコシステムではうまく機能している
      言語レベルでグローバルなパッケージデータベースが不要になる利点もある。example.com/your-thingにパッケージを置けば、そのままリリースされるようなものだ
      もちろん望むなら、キャッシュや検索エンジンは別途提供できる
    • Rustを頻繁に使うわけではないが、こういう問題はパッケージリポジトリ全般で本当に面倒だ
      http-serverはよくないから使わず、MuffinTopを使うべきだ、というような話で、それをただ知っていなければならない
      公認されたパッケージ名という概念は興味深いが、時間がたってエイリアスの背後にあるコードが変わると、実際には混乱を招く可能性が高い
      結局、どのエコシステムでもドメインの専門家になる過程の一部として残り続ける気がする
  • ワークスペースのルートに.cargo/config.tomlを作るとすべてのクレートに適用されるので、グローバルなClippy lintを設定できる
    ファイル内では[build]の下にrustflags = ["-Wclippy::lint_name_to_warn", "-Dclippy::lint_name_to_deny"]のように入れればよい
    ただし、rustflagsは追加ではなく上書き方式なので、RUSTFLAGS環境変数のような別の出典があると、この設定を上書きする

    • 私たちはコミット時にlib.rsmain.rsファイルへlintを追加するスクリプトを走らせている。簡単だ
  • Rustが職業的に重要になるのは明らかに見えるので学んでいる。本当に好きになりたいし、長所も見えているが、これまで使ってきた言語の中でも最も不快な部類に入る。
    習熟すれば嫌いな気持ちが消えることをずっと期待していたが、学習曲線を登るほど、特に愛着が湧くわけではない。
    それでいい。上手に扱えても嫌いな言語は、これだけではないはずだ。ただ、Rustを愛していると言う人があまりに多いので、自分も楽しめると思っていた。

    • 反例として、私はRustプログラミングが好きだ。借用チェッカーと格闘していた時期はずっと前に終わっていて、最近は型を確認するために意図的に起こす場合を除けば、エラーもまれだ。
      Rustコンパイラも、有効なより広いケースを受け入れるように改善されたように思う。
      借用チェッカーを理解する鍵は、その基盤にあるメモリモデルを理解することだった。Rustのメモリモデルは、ジェネリクスのような抽象化のための拡張を除けばCと同じだ。
      借用チェッカーの規則は最初は恣意的に見えるが、このメモリモデルと深く結び付いている。意図せず借用チェッカーに引っかかる時こそ本当に価値がある。注意が散漫になって作ったバグだからだ。
      恐ろしいのは、CやC++のような言語なら、そのようなコードをそのまま受け入れて先へ進めてしまう点だ。
      Rustの厳格な型システムと借用チェッカーは、コードを正しく構造化するよう穏やかに後押ししてくれ、自分が使うあらゆる言語でコード設計を改善してくれたと確信している。
    • Rustを習得しようと何度も試みたが、毎回弾き返される。単に使い勝手が悪い感じがする。
    • いまだに引っかかるのは、デフォルト値/名前付き関数引数がない点だ。
      人気のあるほとんどの言語が共有している、非常に基本的なプログラマ向けの使いやすさの機能で、C++でさえ昔からデフォルト引数を持っている。
    • 何が嫌なのか気になる。
  • 10年間、ほぼ毎週pthreadを呼び出していた元C/C++プログラマだが、今は非同期Rustをどこでも使っている。
    非同期がなぜあれほど嫌われるのか分からない。私の考えでは、誰もがあらゆるものに非同期を使うべきだ。見かけ上は単一スレッドの「単純な」作業まで含めて。

    • 問題は伝染性だと思う。特に組み込みやWasm環境では、主流の非同期が自分の望む非同期ではない場合がある。
      筆者のユースケースがWasmだったなら、間違いなく別の見方になっていたはずだ。
      大きなバッファを使ったり再利用したりしてアロケーションコストを避ける作業は、昔ながらのスレッドプールの恩恵を受けることも多い。バンプアロケータやシャードアロケータである程度は解決できるが、ベクトル化可能なタイトなループでCPUバウンドになる場合は、スレッドプールのほうがうまく動く。
      非同期は良い道具だが、最適な文脈ばかりではない。
    • ネストした非同期呼び出しでのエラー処理がGoとどう違うのか比較しようとして、最近非同期Rustを少し使ってみたが、書こうとしていた些細な例でさえTokioなしではできないように見えた。
      Go、C#、TypeScriptにはそのような障壁はない。
      例えばTokioから持ってくるデコレータなしでは、main関数の中でawaitすることすらできないようだ。
    • 今ぶつかっている問題は、rhaiのEngineのようないくつかの項目がSendではないのに、それを非同期クロージャの中で使おうとしている点だ。
      GPT-4は、スレッド内にtokio Runtimeを作り、block_on()を使うよう提案した。明日試してみるつもりだ。これが自分にとって初めての本格的なRustプロジェクトだ。
    • 私は逆に学んだ。プログラマは必要もないのに何でも不必要に非同期にして、複雑さと認知的負荷を増やす傾向がある。
    • もう少し詳しく説明してもらえるだろうか。見かけ上は単一スレッドの単純な作業のうち、非同期を選ぶべき例が何なのか気になる。
      「今は非同期Rustをどこでも使う」という言葉が、「Tokioをどこでも使う」という意味なのかも気になる。
  • Rustプログラミングは虐待的な関係とはまったく違う。コンパイラは最大限助けようとしてくれるし、特にrustcのエラーメッセージは世界最高水準だ。

    • オペレーティングシステムはプログラマがリソースを正しく扱うことを望んでおり、Rustコンパイラはそれを非常に簡単にしてくれる。
      虐待的な関係により近いのは、Rustコンパイラではなくオペレーティングシステムのほうだ。ハードウェアも同様にアセンブリを正しく実行しなければならないので、そういう見方をすれば虐待的な関係と言える。
      Rustのエラーメッセージは唯一無二で、近づけるコンパイラもない。
      付け加えると、最近LaTeXを使ったが、エラーメッセージはひどかった。何が間違っているのかをエラーをにらみながら把握する過程は悪夢のようだった。
    • 概ね良いが、非同期関数内のどんなエラーでも、すべての再帰呼び出し地点でFutureがもはやSendSyncではないというエラーを生み出すのは本当に嫌だ。
      コンソール全体がエラーで埋め尽くされ、本当の構文エラーはその中間のどこかに埋もれている。
    • Rustコンパイラは、私が見た中で初めて「perhaps」という単語を使うコンパイラだった。
      主な不満は、いまだにライフタイムを完全には理解できていない点で、コンパイラも毎回助けられるわけではない。理解はできるが、コンパイラが保守的に判断するからだ。
  • C++ のテストでは、よく「コンパイルが通ればだいたい正しい」と言う
    Rust が多くのエラーを処理してくれるので、よくあるテストケースが無意味になると思うなら、それは他の言語で正しいものをテストしていなかったというサインだと思う
    テストすべきなのは言語自体の問題ではなく、ビジネスロジックである
    テストコードを見て「JavaScript ならテストするが、Rust ではしなくていい」と感じるなら、そのテストは単に削除すればよい

    • 「コンパイルが通ればだいたい正しい」という言い方は Haskell と Rust では聞いたことがあるが、C++ に当てはめるのは聞いたことがない
    • ヌルポインタ例外はビジネスロジックを壊すバグである
      「ビジネスロジック対言語の問題」という区別はない。言語はビジネスロジックが載る土台だからだ
      失敗の仕方についてテストしないなら、テストに何の意味があるのか分からない
    • 壊れたゴミコードがコンパイルされたのだから、おそらく正しいだろうと考える C++ プログラマー というタイプには、奇妙なすごみがある
      慣れ親しんだ多くの言語と違って C++ には IFNDR があり、冗談めかして「これは C++ プログラムか?」という問いに対する偽陽性と呼ばれる
      標準に準拠した C++ コンパイラは、書いたコードが意味をなしていないと疑われる一部のケースについて知らせることを禁じられており、そのまま処理を続けて何かを出力しなければならない
      それは動く実行ファイルかもしれないし、毎週金曜日に大惨事を起こす実行ファイルかもしれない。知る方法はない
      ISO 標準はこうしたケースを識別してはいるが、あまりに曖昧で何が含まれるのか正確に把握しにくく、私の推測では、現在のほとんどの非 trivial な C++ ソフトウェアは実際には IFNDR だろう。言語全体を拒否したほうがよい
  • Rust は、コンパイラが行うすべてをプログラマーが完全に制御し、意識していなければならないという考えを、ついに打ち破ったように思う
    実際には何十年も前からそうではなく、コンパイラはほとんど魔法に近かった。Rust は借用によってその流れを大きく巻き戻し、人々がコンパイラは自分よりよく知っているという点に安心できるようにした
    さらに安心できるようになってほしい。アルゴリズム上必要な場合でなければ、コレクションを明示的に先頭から反復する必要がないようにすべきだ。多くの処理は暗黙的に並列化されるべきだ。Rust らしい Bash があればよいと思う

    • 私の Rust 経験とはまったく逆である
      Rust は自分が何をしているかがかなり透明で、コンパイラの魔法には非常に保守的だ。言語はヒープ割り当てをせず、参照カウントもしないし、暗黙の数値型変換もない
      暗黙にコピー可能だと宣言していない型はコピーせず、それも単純な浅い memcpy でコピー可能な型でのみ合法である
      Rust は至るところでゼロコスト抽象化を使うため、どのようなコードにコンパイルされるか予測しやすく、たいていは単純だ。標準型の基本的なレイアウトもよく知られているので、Vec の走査はポインタを増やすループにコンパイルされると分かるし、暗黙の並列性はない
      借用を「コンパイラがプログラマーよりよく知っていること」と表現するのは奇妙だ。借用は型検査に似ている。ある型を一時的なものだと宣言しておきながら、長く生きるもののように使おうとすればエラーになる
      Foo 構造体を返すと宣言した関数で Bar を返すとエラーになるのと同じだ。コンパイラが「よりよく知っている」理由は、単にバグを書いたからである
      借用もガベージコレクションなしに直接ポインタ使用へコンパイルされ、C ABI の構造体と関数では文字どおり C ポインタと同一であることが保証される。コンパイラより自分のほうがよく知っていると思うなら、unsafe でライフタイムを回避することもできる
    • 並列イテレータを提供するクレートはすでにある。iter() 呼び出しの名前を変えるだけでよい
      ただし、それが暗黙的であるべきだという点には同意しない
    • 「コンパイラがすることを制御できない」という表現は少し大げさだ
      ある意味では、Rust は C よりもプログラマーに多くの制御を与える。たとえば Rust はインラインアセンブリを標準でサポートするが、C のインラインアセンブリはベンダーごとの拡張に依存する
      ただし、便利なデフォルトは大きく異なる。Rust で安全でない型キャストを行うには多くの手順と慎重さが必要で、C より多くの規則に従わなければならない
      特に Rust の参照は事実上すべて restrict のように動作し、生ポインタから安全な参照へ unsafe キャストする際にこれを壊すのは非常に簡単だ。そのため、選択肢があるならそうしたコードを書かない強い動機が生まれる
    • タグ付きポインタのようなものも含め、unsafe Rust を多用する立場としては強く同意しない
      むしろ、より明示的な制御が欲しいし、より表現力のある型システムでその負担を減らしたい。理想的には Rust の型システムが Prolog の変種のようになってほしい
    • 「Rust らしい Bash」なら、型、とりわけ浮動小数点があり、例外的な状況が少なく、明示的な引数を持つ関数と単純なコマンドラインフラグがある Bash だろう
  • いつ型制約に投資し、いつしないかを学ぶのは重要な教訓である
    Rust だけの問題ではないが、表現の仕方は少し異なるかもしれない
    過度に型付けされた C++ と、過剰に抽象化され型付けされた Java を扱ったことがあるが、どちらも同じ種類のリファクタリング問題を抱えている
    逆に、型が不足しドキュメントも不足している Go も多く見てきたが、特定の値があちこちに散らばってランタイムの地雷になり、リファクタリングが本当に深刻に難しくなることがある
    初期の進捗感はより早く得られるが、たいていはバグをユーザーに配布することになる
    このトレードオフに魔法のような正解はない。Rust はこの軸でかなり幅広い選択肢を提供しているほうだ

  • 「Rustは一日中、毎日、以前の生活では完全に普通だと思っていたことについて叫び続ける」という話は、優れたCコンパイラでもすべてのフラグを有効にすれば似たようなことをする
    選択的に叫ぶのを止められて、意図的に悪いコードを書けるようにしてくれる言語とコンパイラが好きだ。素早く書ける、動く悪いコードは、完璧だが永遠に時間がかかるコードより良いことが多い
    悪いが動く概念実証を作ってから、より悪くない形に直せばよい
    「新規ユーザーはうまく呼び込めるが、ライブラリやツールが劇的に良くなるわけではなく、特定のユースケースを処理する一回限りのフォークだけが生まれる」という点は、年数とは無関係だ
    コア開発者を引きつけるのは難しく、魅力的にするには多くの努力が必要だ。さらに文化的な慣習は初期採用者が決めるもので、慣習がないことも悪い慣習と同じくらい有害な場合が多い
    Pythonを見ると、開発環境と実行環境に緩く向き合った結果、Pythonプログラムを開発または実行する方法が50個ほど競合するようになった
    最も広く使われているパッケージリポジトリであるPyPIは何年も混乱しており、既存パッケージの上に積み上げる人は少なく、名前はランダム単語生成器のように見え、エコシステムには悪意あるコードが多く、コマンドラインからパッケージ検索すらできない
    これは言語のせいではなく、傍観者のようにしていたコミュニティとコアチームのせいだ。文化はその中心にある技術よりも重要だ
    Pythonだけを取り上げて批判したいわけではなく、その問題をよりよく知っているからだ。Cは半世紀にわたって存在しているが、そのコミュニティも、より現代的な言語が用意した解決策の半分もきちんと整理できていない

    • Rustもそれをサポートしている。全部 unsafe とマークすればよい
    • 「改善されたライブラリがない」ことを理由に、LinuxとWindowsがコンポーネントをRustへ移植し始めたとは考えにくい
    • 思い切って言えば、それは TypeScript にかなり似ている
      コンパイラは直せと叫ぶが、完璧に磨き上げる前に、ただアイデアを試してみたいという状況のことだ