Rustチュートリアル: メモリの安全性と並行性

最終更新: 04/12/2025
  • Rust garantiza seguridad de memoria en compilación mediante propiedad, préstamo y lifetimes, sin usar recolector de basura.
  • El sistema de tipos y las reglas de aliasing permiten concurrencia sin data races usando mutex, canales y punteros inteligentes.
  • Cargo, crates.io y un ecosistema activo simplifican la gestión de dependencias, compilación, pruebas y despliegue.
  • Comprender structs, enums, Option y Result es clave para manejar errores y modelar datos seguros en aplicaciones concurrentes.

Cプログラミング言語とRust:メリットとデメリット

Rustは、 すべてのシステム開発者は、これを何度も聞くことになります。CやC++と同等の高速性を備えながら、メモリの安全性と並列処理の精度にほぼ強迫観念的なまでに重点を置いています。これは単なる宣伝文句ではありません。コンパイラがコンパイル時にエラーを検出することを中心として設計されています。他の言語では、システムが既に運用されている状態、あるいはクラッシュした状態でしか確認できないエラーです。

理解に興味がある方 Rust がガベージコレクションなしで安全なメモリとデータ実行を恐れずに並行性を実現する方法このチュートリアルはまさにそんなあなたにぴったりです。言語とそのエコシステムの基礎から、所有権、借用、複合型、Cargoなどのツールといった重要な概念まで、セキュリティとパフォーマンスに焦点を当てながら、並行処理の初心者にも分かりやすい視点からアトミック型とロックまで、あらゆる内容を網羅します。

Rust チュートリアル: パフォーマンス、メモリ安全性、並行性

Rustはプログラミング言語です プログラミング 汎用性とマルチパラダイム性を備え、 低レベルのシステムプログラミングから高レベルのプロジェクトまでから OSのこれは、ゲーム エンジンやブラウザーから高性能 Web サービスに至るまで、特にブラウザー エンジンのような機密性の高いコンポーネントにおけるソフトウェア セキュリティの向上を目的として Mozilla で生まれました。

その特徴は コンパイル時にメモリの安全性を保証する ガベージコレクタを使用せずに、Rustは所有権システムと、各値とその参照の存続期間を追跡する借用チェッカーを採用しています。これにより、自動参照カウントやガベージコレクションを必要とせずに、ダングリングポインタ、バッファオーバーフロー、メモリリークといった典型的な問題を回避できます。

さらに、Rustは、 安全な並行性型と所有権モデルは、少なくとも安全なRustコードの範囲内では、スレッド間のデータ競合を防ぎます。つまり、多くの危険な状況は、1行も実行されないうちにコンパイル時に検出されます。

こうした理由から、大企業は Dropbox、Microsoft、Amazon、または グーグル 彼らはインフラの重要な部分にRustを採用しています。Rustが開発者に「最も愛されている」言語の一つとしてStack Overflowの投票で長年トップを占めているのは、決して偶然ではありません。RustはC++スタイルのパフォーマンスと最新のツールセット(Cargo、crates.io)を融合し、Rustaceansと呼ばれる非常に活発なコミュニティを擁しています。

基本概念: プログラミング言語、型、メモリ

メモリセキュリティと並行性の詳細を掘り下げる前に、本書全体を通して登場するいくつかの一般的な概念を明確にしておく価値がある。 時間 Rustを使うときは、 特に他の言語から来た人やプログラミングを始めたばかりの人.

プログラミング言語は、結局のところ、 アルゴリズムを記述するための一連の規則と構造 Rustはコンパイラを使ってネイティブマシンコードにコンパイルし、実行可能なプログラムに変換します。 rustcしたがって、得られるパフォーマンスは通常、C および C++ と同等になります。

メモリ管理とは、プログラムが 実行中にメモリブロックを予約および解放するこの領域におけるエラーは、メモリリーク(未使用メモリの解放失敗)、境界外書き込みによるデータ破損、解放済みメモリの使用など、致命的となることがよくあります。Rustは、非常に強力な型システムと、所有権、借用、ライフタイムに関する正式なルールによってこの問題に対処しています。

Rustには次のような用語もあります スマート型とポインタ型は、変数がどのような種類のデータ(整数、浮動小数点数、文字列、構造体など)を格納し、どのように操作できるかを記述します。スマートポインタ(例えば、 Box, Rc y Arc) は、メモリ アドレスをカプセル化し、共有参照のカウントやヒープへの値の移動など、リソースを安全に管理するための追加ロジックを追加する構造体です。

競争の分野では、次のような概念が 競合状態、ミューテックス、チャネル これらは不可欠になります。複数のスレッドが適切な調整を行わずに同時に共有リソースにアクセスして変更すると競合状態が発生します。ミューテックス (相互排他) により、一度に 1 つのスレッドだけがクリティカル セクションに入ることが保証されます。チャネルにより、直接メモリを共有せずにスレッド間でメッセージを送信できます。

Rustを学ぶ理由: メモリ安全性と大胆な並行性

Rustは次のような特徴を備えているため、その名声を得ています。 現代のプログラミングにとって非常に価値のある3つの柱パフォーマンス、セキュリティ、そして最新のツール。これらの点がなぜ重要なのか見ていきましょう。

パフォーマンスに関しては、Rust ネイティブバイナリに直接コンパイル 仮想マシンやインタープリタを必要としません。ゼロコスト抽象化モデルは、高レベルの抽象化が実行時にオーバーヘッドを発生させないことを目的としており、システム開発に最適です。 ゲーム、ブラウザ コンポーネント、または低レイテンシのマイクロサービス。

メモリセキュリティは、 所有権と貸借制度ガベージコレクタはありませんが、コンパイラは各リソースの所有者、不要になった時期、解放できる時期を正確に把握しています。これにより、リーク、ダングリングポインタ、そして従来C/C++プログラミングを非常に危険なものにしてきた多くのエラーを回避できます。

競争の分野では、Rustは通常、 「恐れのない同時実行」型システム自体が、セキュアコード内にデータルートが存在することを防止します。スレッド間で可変デー​​タを共有したい場合は、次のような適切なプリミティブを使用する必要があります。 Mutex, RwLock o Arcコンパイラは、エイリアシングと可変性のルールが尊重されることを保証します。

  MalwarebytesでWindowsを駆除する方法

開発エクスペリエンスは、次のような最新ツールによって強化されます。 貨物統合されたパッケージマネージャーとビルドインフラストラクチャ、そして非同期ネットワーク(Tokyo)からWebフレームワーク(Actix、Rocket、Axum)まであらゆるものをカバーするライブラリ(クレート)の幅広いエコシステムを備えています。これらすべては、特に初心者にとって、オープンで活発、そして非常に忍耐強いコミュニティによって支えられています。

インストールと必須ツール: rustup、rustc、Cargo

Rustで最初のプログラムを書いて実行するには、通常は公式ツールチェーンをインストールすることから始めます。 錆びたRustの完全入門)、すべての主要オペレーティング システムで動作するシンプルなインストーラーおよびバージョン マネージャーです。

とともに 錆びた Rustの異なるバージョン(安定版、ベータ版、ナイトリー版)をインストール、アップデート、切り替えても、何も問題はありません。公式Rustツールページにアクセスし、お使いのシステムに応じた手順に従ってください。インストールが完了すると、コンパイラが利用可能になります。 rustcプロジェクトマネージャー cargo そして彼自身 rustup あなたの中に ターミナル.

コンパイラ rustc ソースコードを実行可能なバイナリやライブラリに変換するものです。直接呼び出すこともできますが、 コマンド として rustc main.rs実際には、ほとんどの場合、Cargoを介して作業します。Cargoは、 rustc 適切なオプションを選択します。

ワークフローの中心的なツールは 貨物ほんの数個のコマンドで、新しいプロジェクトの作成、依存関係の管理、コンパイル、実行、テスト、そしてcrates.ioへのパッケージの公開が可能です。よく使われる基本コマンドは以下のとおりです。 cargo new, cargo build, cargo run, cargo test y cargo checkは、最終的な実行可能ファイルを生成せずにコードをチェックするため、エラーを迅速に検出するのに最適です。

何もインストールせずにいじりたい場合は、 錆びた遊び場 (公式オンライン エグゼキューター) や Replit などのプラットフォームを使用すると、ブラウザーから小さなコードを作成して実行することができ、環境全体をセットアップすることなくメモリや同時実行の例を試すのに最適です。

最初のプログラム: Hello, Rust と基本的な流れ

どの言語でも会話を始める古典的な方法は有名な「Hello, world」です。Rustでは、ファイル main.rs 最小値には関数のような単純なものも含まれる可能性がある main 画面に文字列を出力する.

キーワード fn 関数を定義していることを示します。 main これはプログラムのエントリポイントです。関数のコードブロックは中括弧で囲みます。コンソールに書き込むには、 マクロ println!は、文字列リテラル (またはブックマーク付きのテンプレート) を受け入れ、改行文字で終わる標準出力に送信します。

直接コンパイルする場合は rustc main.rs実行可能なバイナリファイル(例えば、 main o main.exe (システムによって異なります)。実行すると、ターミナルにメッセージが表示されます。しかし、Rust を使う際の慣用的な方法は、Cargo にプロジェクトを主導させることです。

とともに cargo new nombre_proyecto フォルダ構造は自動的に作成され、 src/main.rs 「Hello, world」とファイルがすでに用意されている Cargo.toml メタデータと将来の依存関係が含まれています。そこから cargo run バイナリをコンパイルして実行する変更が検出された場合にのみ再コンパイルされます。

この作業方法は便利なだけでなく、最初から標準の Rust エコシステムの使用に慣れることができるため、並行性、ネットワーク、テストなど、必要なもののためのクレートを追加し始めるときに非常に役立ちます。

// メイン関数を宣言します: プログラムのエントリ ポイント fn main() { // println! マクロを使用して、コンソールにテキストを出力します println!("Hello, world!"); }

変数、可変性、基本データ型

Rustでは変数はキーワードで宣言されます let、そしてデフォルトでは 不変であるつまり、一度値を割り当てたら、明示的に変更可能と宣言しない限り、変更することはできません。 mut.

デフォルトで不変性が確保されているため、特に複数のスレッドが同じ値を変更する可能性がある並行プログラムでは、微妙な論理エラーを回避するのに役立ちます。値を変更する必要がある場合は、次のように記述します。 let mut contador = 0;そこから新しい値を再割り当てすることができます contador.

Rustでは、いわゆる シャドーイング同じスコープ内で同じ名前の新しい変数を宣言し、以前の変数を隠蔽することができます。これは新しい値(異なる型であっても構いません)を作成するため、変更とは異なります。例えば、同じ名前を使用して文字列から整数に変換できます。ただし、新しい宣言で let.

Rustの型システムは静的であり、つまり 各変数の型はコンパイル時に判明するしかし、型推論は非常に強力です。 let x = 5;コンパイラは、それが i32 他に指示がない限り、次のようなメモを追加できます。 let x: i64 = 5; 明確に伝えたいとき。

利用可能なスカラー型には符号付き整数と符号なし整数(i8, u8, i32など)、浮いているもの(f32, f64)、ブール値(bool) および Unicode 文字 (char). これらの単純な型は通常、コピーが簡単で、多くの型は特性を実装しています。 Copyつまり、これらを割り当てたり関数に渡したりすると、移動されるのではなくコピーされます。

Rustの文字列: &strとString

Rustでのテキスト処理は、最初は少し混乱するかもしれません。なぜなら、 チェーン「スライス」と独自のチェーン2つの鍵となるのは &str y String.

Un &str あります 不変の鎖の一部どこかに保存されているUTF-8バイトシーケンスのビュー。典型的な例としては、次のようなリテラルが挙げられます。 "Hola"これらは、 &'static str (スライスはプログラムの存続期間中存在し、バイナリに埋め込まれます。) スライスはデータを所有せず、データを指すだけです。

  Windows 11のタスクバーを徹底的にカスタマイズする方法:完全ガイドと高度なヒント

String一方、は 独自の文字列、変更可能、ヒープにホストされるサイズ変更、連結、プロパティの移動による関数間の受け渡しなどが可能です。動的なテキストを構築したり、構造体内に長期間保存したりする場合によく使用されます。

多くのシナリオでは、一方から他方への変換を行います。例えば、 String::from("hola") スライスからまたは借りる &str A String 読み取りのみが必要な関数への参照を渡すことによって。

所有データと借用データの分離はメモリ管理の鍵であり、言語の残りの部分にも拡張されます。コレクション、構造体、列挙型は、誰が所有し、誰が参照のみを行うかという同じ考え方に従います。

関数、制御フロー、コメント

Rustの関数は次のように定義されます fn プログラムを再利用可能な論理ユニットに編成することを可能にする。各関数は以下を指定する。 パラメータの型と戻り値の型 矢印に従って ->意味のあるものが何も返されない場合は、ユニタリ型であると想定されます。 ().

重要な点は、関数(または任意のブロック)内のセミコロンのない最後の式が暗黙の戻り値として扱われることです。 return 早期返却の場合しかし、慣用的なコードでは、最終的な式をそのままにしておくことがよくあります。 ;.

制御フローは古典的な方法で処理されます if/elseループ loop, while y forRustでは、 if 値を返す式です直接使用できるので、 let分岐が同じ型を返すことを条件とする。ループ for これらは通常、範囲またはコレクション反復子を反復処理するため、手動インデックスの代わりに推奨されるオプションです。

コードを文書化して、後から来る人(1ヶ月後の自分も含む)の作業を楽にするために、 行コメント // またはブロックする /* ... */さらに、Rustはドキュメントコメントを提供します。 /// これらは生成されたドキュメントになりますが、大規模なプロジェクトに適しています。

所有権、貸与、そして生涯:記憶のセキュリティの基盤

ここで、Rustのメモリモデルの核心である、 所有権、借入、そして生涯これらのルールにより、参照が常に有効になり、ガベージが蓄積されることなくメモリが安全に解放されることが保証されます。

所有権の基本的なルールは、最初は理解するのが難しいかもしれませんが、述べるのは簡単です。 各値には単一の所有者が存在します。所有者は一度に1人しか存在できません。所有者がスコープを離れると、値は破棄され、メモリは解放されます。これは例えば、 String: 宣言されたブロックが完了すると、自動的に呼び出されます drop これによりヒープメモリが解放されます。

適切な値を別の変数に代入したり、関数に値渡ししたりすると、プロパティは移動されます。これはつまり、 移動後、元の変数は無効になりますこの移動セマンティクスにより、2 人の所有者が同じリソースを解放しようとすることはなくなるため、二重の解放が回避されます。

プログラムの複数の部分から所有権を変更せずに同じ値にアクセスできるようにするために、Rustは参照と借用を導入しています。借用とは、参照を作成することを意味します。 &T (不変)または &mut T 所有権を譲渡せずに値に変更します。 ローンはローン検証者の規則によって制限されます。は、参照がそれが指すデータよりも長く存続しないこと、および可変アクセスと共有アクセスが危険なほど混在していないことを確認します。

ローンのルールは次のようにまとめられます。いつでも、 複数の不変参照 値に、または 単一の変更可能な参照ただし、両方を同時に行うことはできません。これにより、共有メモリにおける競合状態が解消されます。つまり、多数の読み取りアクセスが存在するか、独立した書き込みアクセスが存在するかのいずれかであり、同じデータに対して同時に読み取りアクセスと書き込みアクセスが行われることはありません。

複合型: 構造体、列挙型、スマートポインタ

Rustは、関連するデータをより豊富な構造にグループ化するためのいくつかの方法を提供します。 構造体構造体を使用すると、電子メール、名前、アクティビティ ステータス、ログイン カウンターを持つユーザーなど、名前付きフィールドを持つカスタム タイプを定義できます。

構造体のインスタンスを作成するには、すべてのフィールドに値を入力します。また、構造体を含む変数を可変としてマークすることで、後で値を変更することもできます。また、構造体の更新構文を使用すると、既存のインスタンスのフィールドを再利用して新しいインスタンスを構築できます。 ..otro_struct.

たくさん 列挙型 これらはもう一つの重要な柱です。これらによって、複数の可能な変種のいずれかを持つ型を定義できます。それぞれの変種には、独自の関連データがあるか、または関連データがありません。典型的な例としては、IPアドレスの列挙型があります。 V4 4つのオクテットと別の V6 IPv6 表記の文字列を格納します。

Rust の標準ライブラリには、2 つの非常に重要な列挙型が含まれています。 Option<T> y Result<T, E>最初のものは値の有無(何かまたは何もない)を表し、ヌルポインタを避けるために使用されます。2番目のものは、 正しい結果またはエラーを返すエラー処理が明示的かつ安全であることが必要です。

動的メモリを管理し、データを共有するために、Rustは スマートポインタ として Box<T>、値をヒープに移動し、一意の所有権を維持します。 Rc<T>、シングルスレッド環境用の共有参照カウント。 Arc<T>に似ています Rc ただし、複数のスレッドに対しては安全です。動的メモリと並行処理を組み合わせる場合は、これらを正しく使用することが重要です。

貨物と木箱のエコシステム

Cargo は Rust エコシステムをまとめる接着剤のようなものです。 コンパイル、依存関係、プロジェクトのライフサイクルを管理する各プロジェクトにはファイルがあります Cargo.toml これはマニフェストとして機能し、名前、バージョン、言語エディション、および外部依存関係を宣言します。

  修正: Windows 10 内部 PCI バス ドライバー エラー

セクション このファイルでは、サードパーティ製のクレートとそのバージョンを一覧表示できます。 cargo build o cargo runCargoはこれらのクレートをcrates.ioから自動的にダウンロードし、コンパイルしてプロジェクトにリンクします。乱数ジェネレーター、Webフレームワーク、暗号化ライブラリなどを追加するのも簡単です。

最も一般的なコマンドは cargo new バイナリプロジェクトを開始する o cargo new --lib 図書館向け cargo build デバッグモードでコンパイルします。 cargo build --release 最適化された生産指向のバージョンを取得すること。 cargo test 一連のテストを実行します。

cargo check 特筆すべき点は、バイナリを生成せずにコードを中間点までコンパイルするため、 コンパイルエラーの検出が非常に速い借用チェッカーがプロパティ、参照、有効期間に関する問題を指摘している間に、すばやく反復するのに最適です。

このエコシステムのおかげで、プロジェクトを小さく明確に定義されたクレートとして構造化し、それら間でコードを共有し、コミュニティによって作成されたソリューションを再利用することが一般的になっています。例えば、高度な並行処理を実現するには、非同期プログラミング用のTokioや、高性能な並行データ構造用のcrossbeamといったクレートが利用できます。

Rust における並行性: スレッド、ミューテックス、チャネル、アトミック

並行性は、Rust が大きな関心を集めている理由の 1 つです。並行性により、マルチコア プロセッサを活用できるようになります。 スレッドや共有メモリの典型的なエラーに陥ることなくこれらのトピックに初めて取り組む場合は、いくつかの概念を区別しておくと役立ちます。

並行処理とは、1つまたは複数のコア上で、時間的に重なり合う複数のタスクを実行することです。Rustでは、システムスレッドを作成して並列処理を実行でき、それらのスレッド間でのデータ共有が安全であることを言語が保証します。よくあるエラーとして、2つのスレッドが同時にデータにアクセスして変更する競合状態があります。その結果は実行順序に依存し、デバッグが非常に困難です。

共有データへのアクセスを調整するために、Rustは次のようなプリミティブに依存しています。 ミューテックス相互排他性を保証する:クリティカルセクションには一度に1つのスレッドしか入れない。 Arc<T> スレッド間で所有権を共有するには、所有権と借用のルールに準拠した共有データ構造を構築することができます。

Rustで強く推奨されているスレッド間通信のもう1つの一般的な形式は、メッセージパッシングです。 カナレスチャネルには送信側と受信側があり、スレッドはチャネルを介してメッセージ (値) を渡すため、変更可能な共有メモリの使用が削減され、システムの状態に関する推論が簡素化されます。

低レベルの同時実行性をさらに詳しく調べると、次のことがわかります。 原子型アトミック変数は、スレッドの観点からは不可分な操作を通じてアクセスされます。これにより、共有カウンター、状態フラグ、ロックフリーキューなどの実装が可能になります。アトミック変数をマスターするには、メモリモデルとアクセスコマンドの理解が求められるため、多くの開発者は、これらの詳細を掘り下げる前に、まずミューテックスとチャネルから始めることを好みます。

並行性とアトミック性を学ぶための最初のステップとリソース

経験なしで競技に参加する場合、最も賢明な行動は 一般的な概念の確固たる基礎を築く Rustのアトミック型のような高度なツールに取り組む前に、まずはRustの基礎を学びましょう。『Programming Rust』のような書籍は段階的な入門書として役立ちますが、アトミック型やロックに重点を置いた書籍は、最初は難解に感じられるのが普通です。

よりスムーズに進めるために、まずは 従来のスレッド、相互排他、メッセージパッシング Rustで。例を見てみる std::thread, std::sync::Mutex, std::sync::Arc とのチャネル std::sync::mpsc コンパイラがどのようにガイドし、どのようなエラーを回避するかを理解するのに役立ちます。

並行して、Rust に焦点を当てていなくても、一般的な並行性に関する入門リソースを確認することを強くお勧めします。競合状態とは何か、ブロッキングが何を意味するのか、共有メモリとメッセージ パッシングの違いは何か、ロックがどのように使用されるかを理解する必要があります。 これらの概念があなたにとって自然になれば、原子物理学は「黒魔術」ではなくなります。 そしてそれらは、非常に繊細なツールに過ぎなくなります。

Rust のアトミック性とロックに関するより高度なテキストに戻るとき、単純なスレッドセーフなカウンターから競合を最小限に抑えるロックフリーの構造まで、各構成要素がどのような問題を解決しようとしているのかをすでに理解していれば、推論を理解するのがはるかに簡単になります。

結局のところ、Rust は高レベルのプリミティブと非常に低レベルのツールの両方を提供しており、重要なのは、アトミック コードに頼って、問題を解決する最も安全な抽象化レベルを常に選択することです。 unsafe それが本当に価値を付加し、その意味を完全に理解した場合にのみ有効です。

この型、所有権、借用、クレート、ツール、並行性プリミティブのエコシステム全体が組み合わさって、次のような言語が提供される。 高速で堅牢かつ保守性の高いソフトウェアこれにより、これまでシステムプログラミングを悩ませてきた多くの種類のエラーを最小限に抑えることができます。小規模プロジェクト、Rustlingsなどの演習、公式ドキュメントなどで練習を重ねていくうちに、これらの概念は厳格なルールのように思えるものから、問題が本番環境に到達する前に警告を発してくれる味方へと変わっていくでしょう。

Rust言語入門(例題付き)-0
関連記事
Rustの完全入門:例を交えた実践的な初心者向けガイド