Rust言語用語集:技術解説
【本文書の使い方】
本用語集は、Rustプログラミング言語に関する基本用語と重要な特徴機能を解説する技術文書である。Rust固有の概念に加え、プログラミング基本概念の説明も含めている。各用語には詳細な説明とコード例が付属しており、用語間のリンクをたどることで関連概念を効率的に学習できる。プログラミング初学者から中級者を対象とする。
推奨される学習順序:
- 基本的なデータ型と変数の概念を理解する(型、変数、初期化)
- 所有権システムの基本概念と規則を理解する
- 借用とライフタイムの仕組みを学ぶ
- 制御文の仕組みを学ぶ(制御文、if、for、while、match)
- 関数の定義と呼び出しを習得する(関数、パラメータ、引数)
- エラーハンドリングの方法を習得する
- イテレータとクロージャを活用する
- 構造体とトレイトの概念を学ぶ(構造体、トレイト、実装)
- 並行性の概念を理解する
辞書的利用:用語集から必要な用語を直接参照することも可能である。
【目次】
主要概念の解説
本セクションでは、Rustプログラミング言語の重要な特徴機能を解説する。これらの概念は相互に関連しており、Rustのメモリ安全性と高性能を実現する基盤となっている。
所有権システム(Ownership System)
Rustの所有権システムは、メモリ安全性を保証する中核的な機能である。各値には唯一の所有者が存在し、所有者がスコープを抜けると値は自動的に解放される。この仕組みにより、ガベージコレクタなしでメモリリークやダングリングポインタを防ぐことができる。
所有権の基本ルール
- 各値には唯一の所有者が存在する
- 所有者は同時に1つのみである
- 所有者がスコープを抜けると、値は破棄される
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1の所有権がs2に移動(ムーブ)
// println!("{}", s1); // エラー:s1はもう使用できない
println!("{}", s2); // OK
}
借用とライフタイム(Borrowing and Lifetimes)
借用は、所有権を移動せずに値への参照を渡す仕組みである。借用には不変借用(&)と可変借用(&mut)の2種類がある。ライフタイムは、参照が有効な期間を示す概念であり、コンパイラが参照の安全性を保証するために使用する。
借用の規則
- 任意の数の不変借用、または1つの可変借用のみが同時に存在できる
- 参照は常に有効でなければならない
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s);
}
fn change(s: &mut String) {
s.push_str(", world");
}
クロージャ(Closures)
クロージャは、環境の変数をキャプチャできる匿名関数である。クロージャはfnキーワードを使わず、|引数| 式の構文で定義する。クロージャは、moveキーワードを使用することで、キャプチャした変数の所有権を取得することもできる。
fn main() {
let add_one = |x| x + 1;
println!("{}", add_one(5)); // 6
let v = vec![1, 2, 3];
let doubled: Vec<_> = v.iter().map(|x| x * 2).collect();
}
エラーハンドリング(Error Handling)
Rustは、回復可能なエラーに対してResult型を、回復不可能なエラーに対してpanic!マクロを使用する。Result型は、OkとErrのバリアントを持つ列挙型である。?演算子を使用することで、エラーを簡潔に伝播できる。
use std::fs::File;
fn read_file() -> Result<String, std::io::Error> {
let mut contents = String::new();
File::open("hello.txt")?.read_to_string(&mut contents)?;
Ok(contents)
}
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => {
panic!("ファイルを開けません: {:?}", error);
}
};
}
イテレータ(Iterators)
イテレータは、一連の要素を順番に処理するための抽象化である。Rustのイテレータは遅延評価され、ゼロコスト抽象化の原則に基づいて実装されている。map、filter、sumなどのメソッドを連鎖させることで、効率的なデータ処理が可能となる。
fn main() {
let v = vec![1, 2, 3];
let sum: i32 = v.iter().map(|x| x * 2).sum();
println!("{}", sum); // 12
let even: Vec<_> = v.iter().filter(|&x| x % 2 == 0).collect();
}
並行性(Concurrency)
Rustの並行性モデルは、所有権システムと型システムを活用することで、データ競合をコンパイル時に防ぐ。spawn関数を使用してスレッドを生成し、チャネルやMutexなどの同期プリミティブを使用してスレッド間通信を行う。
use std::thread;
use std::sync::mpsc;
fn main() {
let handle = thread::spawn(|| {
println!("別スレッドから");
});
handle.join().unwrap();
// チャネルを使用したスレッド間通信
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send("メッセージ").unwrap();
});
let received = rx.recv().unwrap();
}
用語
本セクションでは、プログラミングに関する基本用語を五十音順に解説する。各用語について、Rustでの扱いを中心に説明し、必要に応じて他の言語との比較も示す。
アクセス
データの読み出しや書き込みを行うことをアクセスという。「メモリをアクセスする」、「ディスクをアクセスする」などのように使用する。Rustでは、借用規則により、安全なアクセスが保証される。
イミュータブル
変更不可能な状態をイミュータブルという。Rustでは、変数はデフォルトでイミュータブルであり、明示的にmutキーワードを付けることでミュータブル(変更可能)になる。この仕組みにより、意図しない変更を防ぎ、コードの安全性を高めることができる。
let x = 5; // イミュータブル
// x = 6; // エラー:イミュータブルな変数に再代入できない
let mut y = 10; // ミュータブル
y = 11; // OK
入れ子
ある構造の内部に同じ種類の構造が含まれている状態を入れ子という。ループや条件分岐、関数呼び出しなどで入れ子構造が使用される。
for i in 0..10 {
for j in 0..20 {
println!("i={}, j={}", i, j);
}
}
// 2次元ベクタの作成
let x: Vec<Vec<i32>> = vec![vec![0; 20]; 10];
エディタ
エディタとは、テキストやプログラムを編集する機能を持ったソフトウェアである。Rustプログラミング用のエディタには、Visual Studio Code(rust-analyzerプラグイン)、IntelliJ IDEA(Rustプラグイン)、Vimなどがある。
エラー
プログラムの実行中または実行前に発生する問題をエラーという。Rustでは、エラーを以下のように分類する。
- コンパイルエラー:プログラムの構文上の誤りや型の不一致により、コンパイルが失敗する。
- 回復可能なエラー:Result型で表現され、プログラムで適切に処理できるエラー。ファイルが見つからない場合やネットワーク接続の失敗などが該当する。
- 回復不可能なエラー:panic!マクロで表現され、プログラムが続行不可能な状態。配列の範囲外アクセスやアサーション失敗などが該当する。
演算子
演算子とは、1個または複数のオペランドに対して演算を行うための記号である。Rustの代表的な演算子を以下に示す。
算術演算子
+ 加算
- 減算
* 乗算
/ 除算
% 剰余
比較演算子
== 等しい
!= 等しくない
< より小さい
<= 以下
> より大きい
>= 以上
論理演算子
&& 論理積
|| 論理和
! 否定
ビット演算子
& ビット積
| ビット和
^ ビット排他的論理和
<< 左シフト
>> 右シフト
代入演算子
= 代入
+= 加算代入
-= 減算代入
*= 乗算代入
/= 除算代入
その他
[] インデックスアクセス
. フィールドアクセス、メソッド呼び出し
& 参照
* 参照外し
:: パス区切り
? エラー伝播
オーバーフロー
演算結果の絶対値が、データ型で表現可能な範囲を超えた状態をオーバーフローという。Rustでは、デバッグビルドでオーバーフローが発生するとpanicするが、リリースビルドでは循環的に値が折り返される(2の補数による)。明示的な動作を制御するには、以下のメソッドを使用する。
- wrapping_add:循環的な動作
- checked_add:オーバーフロー時にNoneを返す
- saturating_add:最大値または最小値で飽和する
- overflowing_add:結果とオーバーフローの有無を返す
let x: u8 = 255;
let y = x.wrapping_add(1); // 0(循環)
let z = x.checked_add(1); // None(オーバーフローを検出)
let w = x.saturating_add(1); // 255(最大値で飽和)
オペランド
演算子による演算の対象となる値や変数をオペランドという。例えば、「a + b」という式では、aとbがオペランドであり、+が演算子である。
改行
テキストの表示位置を次の行の先頭に移動させる動作を改行という。Rustでは、文字列中に\nを記述することで改行を表現する。
println!("1行目\n2行目\n3行目");
関連項目:改行文字
改行文字
改行を表す特殊文字を改行文字という。Rustでは\nで表現する。改行の内部表現はオペレーティングシステムによって異なるが、Rustの標準ライブラリは適切に処理する。
型
型とは、データの種類と、そのデータに対して実行可能な操作を定義するものである。Rustは静的型付け言語であり、変数の型はコンパイル時に決定される。これにより、型に関するエラーを実行前に検出できる。Rustの主な組み込み型を以下に示す。
- 整数型:i8、i16、i32、i64、i128、isize(符号付き)、u8、u16、u32、u64、u128、usize(符号なし)
- 浮動小数点数型:f32、f64
- ブール型:bool
- 文字型:char(Unicodeスカラ値)
- タプル型:(i32, f64, char)など
- 配列型:[i32; 5]など
- スライス型:&[i32]など
- 文字列スライス型:&str
- String型:ヒープ割り当て文字列
仮引数
仮引数(パラメータ)とは、関数定義において宣言される変数である。関数が呼び出されると、実引数の値がパラメータに渡される。Rustでは、パラメータの型を必ず明示する必要がある。
fn greet(name: &str) { // nameが仮引数
println!("Hello, {}", name);
}
greet("Alice"); // "Alice"が実引数
関数
関数とは、特定の処理をまとめた再利用可能なコードブロックである。引数を受け取り、処理を実行し、結果を返すことができる。関数を使用することで、コードの重複を避け、可読性と保守性を向上させることができる。
【関数の定義】
Rustではfnキーワードを使用して関数を定義する。パラメータの型と戻り値の型を明示する必要がある。
fn add(a: i32, b: i32) -> i32 {
a + b // 最後の式が戻り値(セミコロンなし)
}
let result = add(3, 5); // resultは8
【戻り値】
関数はreturn文で値を返すか、最後の式をセミコロンなしで記述することで値を返す。戻り値がない場合は、ユニット型()を返す。
fn greet(name: &str) {
println!("Hello, {}", name);
// ユニット型()が返される
}
fn multiply(a: i32, b: i32) -> i32 {
return a * b; // 明示的なreturn
}
fn square(x: i32) -> i32 {
x * x // 最後の式が戻り値
}
【関数呼び出しの流れ】
プログラムは通常、上から下へ順番に実行される。関数呼び出しがあると、制御は関数の内部に移り、関数の処理が完了すると呼び出し元に戻る。
擬似乱数
数学的なアルゴリズムによって生成される、乱数のように見える数列を擬似乱数という。Rustではrandクレートを使用して擬似乱数を生成する。
use rand::Rng;
let mut rng = rand::thread_rng();
let x: f64 = rng.gen(); // 0.0以上1.0未満
let y: u32 = rng.gen_range(1..=100); // 1以上100以下
クレート
クレートは、Rustのコンパイル単位である。ライブラリクレートまたはバイナリクレートのいずれかである。クレートは、関連するモジュールの集合であり、Cargo.tomlファイルで依存関係を管理する。
クローン
値の深いコピーを作成することをクローンという。Rustでは、Cloneトレイトを実装した型に対してclone()メソッドを呼び出すことで、値の完全なコピーを作成できる。クローンは、所有権を維持したまま、元の値のコピーを別の変数で使用する必要がある場合に有用である。
let s1 = String::from("hello");
let s2 = s1.clone(); // s1の深いコピー
println!("{}, {}", s1, s2); // 両方とも使用可能
繰り返し
同じ処理を条件に基づいて複数回実行することを繰り返し(ループ)という。Rustでは、for文、while文、loop文が繰り返し処理に使用される。
コーディング
ソースプログラムを作成する作業をコーディングという。プログラミングが設計から実装までの全工程を指すのに対し、コーディングは設計に基づいてコードを記述する工程を指すことが多い。
構造体
複数のデータを1つにまとめたデータ型を構造体という。Rustではstructキーワードを使用して構造体を定義する。構造体を使用することで、関連するデータをグループ化し、コードの意味を明確にすることができる。
struct Person {
name: String,
age: u32,
height: f64,
}
let person = Person {
name: String::from("Alice"),
age: 30,
height: 165.5,
};
println!("名前: {}", person.name);
構造体には、名前付きフィールドを持つ構造体、タプル構造体、ユニット構造体の3種類がある。
コメント
プログラム中に記述する注釈をコメントという。コメントはプログラムの実行時に無視され、コードの説明や使用上の注意を記述するために使用される。
Rustでは、//記号から行末までが1行コメント、/*と*/で囲んだ部分が複数行コメントとなる。
// これは1行コメント
let x = 10; // 行末コメント
/*
これは複数行コメント
複数行にわたって記述できる
*/
/// これはドキュメンテーションコメント
/// 関数やモジュールの説明に使用する
fn example() {
// 処理
}
コンパイル
プログラムのソースコードを、コンピュータが実行可能な形式に変換する処理をコンパイルという。Rustでは、ソースコードは機械語に直接コンパイルされ、実行可能ファイルが生成される。Rustコンパイラ(rustc)は、LLVMを使用して最適化されたネイティブコードを生成する。
再帰的呼び出し
関数が自分自身を呼び出すことを再帰的呼び出し(再帰)という。再帰は、問題を同じ構造の小さな問題に分割して解く場合に有効である。
以下は、ハノイの塔問題を再帰で解くRustプログラムである。
fn hanoi(n: u32, source: &str, auxiliary: &str, target: &str) {
if n == 1 {
println!("円盤1を{}から{}へ移動", source, target);
return;
}
hanoi(n - 1, source, target, auxiliary);
println!("円盤{}を{}から{}へ移動", n, source, target);
hanoi(n - 1, auxiliary, source, target);
}
hanoi(3, "A", "B", "C");
三角関数
角度に関する数学関数を三角関数という。Rustでは標準ライブラリのf32およびf64型のメソッドとして三角関数が提供される。引数と戻り値の角度の単位はラジアンである。
use std::f64::consts::PI;
let x = 0.5_f64;
let sine = x.sin(); // サイン
let cosine = x.cos(); // コサイン
let tangent = x.tan(); // タンジェント
let arcsine = sine.asin(); // 逆サイン
let arccosine = cosine.acos(); // 逆コサイン
let arctangent = tangent.atan(); // 逆タンジェント
算術
数値に対する基本的な計算(加減乗除など)を算術という。
借用
借用は、所有権を移動させることなく、値への参照を作成することである。借用により、値を共有しつつメモリ安全性を保つことができる。借用には不変借用と可変借用の2種類がある。
- 不変借用(&T):値を読み取るが変更しない。複数の不変借用を同時に持つことができる。
- 可変借用(&mut T):値を変更できる。可変借用は同時に1つしか持てず、不変借用と同時には存在できない。
fn calculate_length(s: &String) -> usize {
s.len() // 値を変更せず読み取る
}
fn change(s: &mut String) {
s.push_str(", world"); // 値を変更
}
let mut s = String::from("hello");
let len = calculate_length(&s); // 不変借用
change(&mut s); // 可変借用
参照
参照は、値への借用を表す型である。&演算子で作成し、*演算子で参照外しを行う。Rustの参照は常に有効なデータを指すことが保証される(ダングリングポインタは発生しない)。
let x = 5;
let r = &x; // xへの参照
println!("{}", *r); // 参照外し
let mut y = 10;
let mr = &mut y; // 可変参照
*mr += 5;
println!("{}", y); // 15
式
演算子とオペランドの組み合わせで構成され、評価すると値を生成するコードの断片を式という。Rustでは、ほとんどの構文が式であり、値を返す。この特性により、より柔軟で簡潔なコードを記述できる。
// Rustの式の例
30
x
x + y
a * b + c
vec.len()
x < 100
// ブロックも式
let y = {
let x = 3;
x + 1 // 4を返す
};
// if式も値を返す
let number = if condition { 5 } else { 6 };
識別子
変数、関数、構造体、トレイトなどに付ける名前を識別子という。Rustの識別子は以下の規則に従う。
- 英字(a-z、A-Z)、数字(0-9)、アンダースコア(_)で構成する
- 先頭に数字を使用できない
- キーワードを使用できない(ただし、raw識別子r#を使用すれば可能)
- 大文字と小文字は区別される
- 命名規則:スネークケース(snake_case)を使用する
字下げ
プログラムの行頭に空白やタブを挿入することを字下げ(インデント)という。Rustでは、インデントは構文上必須ではないが、4つの空白を使用することが標準的な慣例である(rustfmtで自動整形される)。
if x < 100 {
println!("xは100未満"); // 4つの空白でインデント
let y = x * 2;
}
実行形式ファイル
機械語で記述され、コンピュータが直接実行できる形式のファイルを実行形式ファイル(実行ファイル)という。Rustでは、コンパイルによって実行形式ファイルが生成される。Windowsでは.exe、Unix系ではELF形式が使用される。
ジャンプ文
プログラムの実行位置を移動させる文をジャンプ文という。Rustのジャンプ文には以下がある。
関連項目:制御文
初期化
変数やオブジェクトに最初の値を設定することを初期化という。Rustでは、変数は使用前に必ず初期化する必要がある(未初期化変数の使用はコンパイルエラー)。これにより、未初期化変数の使用による予期しない動作を防ぐことができる。
let x: i32 = 10; // 整数
let rate: f64 = 124.10; // 浮動小数点数
let name: &str = "Alice"; // 文字列スライス
let numbers: Vec<i32> = vec![1, 2, 3]; // ベクタ
// 型推論も使用可能
let y = 5; // i32と推論される
書式文字列
データを文字列に変換する際の形式を指定する文字列を書式文字列という。Rustでは、format!マクロ、println!マクロなどで書式文字列を使用する。
let name = "Alice";
let age = 30;
// {}プレースホルダ
println!("{}は{}歳です", name, age);
// 名前付き引数
println!("{name}は{age}歳です", name=name, age=age);
// デバッグ出力({:?})
let vec = vec![1, 2, 3];
println!("{:?}", vec);
// 書式指定
let pi = 3.141592653589793;
println!("{:.2}", pi); // 小数点以下2桁:3.14
真
Rustでは、ブール型のtrueのみが真として評価される。他の言語とは異なり、0以外の数値や空でない文字列は真として評価されない。
関連項目:偽、true / false
数字
数字とは、0から9までの10個の記号である。
0 1 2 3 4 5 6 7 8 9
スライス
スライスは、コレクションの一部への参照である。所有権を持たず、元のデータへの窓として機能する。スライスを使用することで、データ全体をコピーせずに部分的にアクセスできる。
let s = String::from("hello world");
let hello = &s[0..5]; // "hello"へのスライス
let world = &s[6..11]; // "world"へのスライス
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3]; // [2, 3]へのスライス
制御文
プログラムの実行順序を変更する文を制御文という。制御文には以下の種類がある。
宣言
変数や関数の名前と型を指定することを宣言という。Rustでは、変数の宣言にletキーワードを使用する。型は明示的に指定することも、型推論に任せることもできる。
let x: i32 = 10; // 型を明示
let y = 20; // 型推論(i32)
let name: String = String::from("Alice");
所有権
Rustの最も重要な概念の一つである所有権は、メモリ安全性を保証する仕組みである。所有権システムにより、ガベージコレクタなしでメモリ安全性とデータ競合の防止が実現される。所有権の規則は以下の通りである。
- 各値には所有者(owner)と呼ばれる変数が存在する
- 所有者は同時に1つしか存在しない
- 所有者がスコープから外れると、値は自動的に破棄される
{
let s = String::from("hello"); // sが所有者
// sを使用できる
} // スコープ終了、sが破棄される
let s1 = String::from("hello");
let s2 = s1; // 所有権がs1からs2に移動(ムーブ)
// println!("{}", s1); // エラー:s1はもう使えない
println!("{}", s2); // OK
ソースファイル
プログラムのソースコードを保存したテキストファイルをソースファイルという。Rustのソースファイルの拡張子は.rsである。
ソースプログラム
人間が読み書きできる形式で記述されたプログラムをソースプログラム(ソースコード)という。Rustでは、ソースコードはコンパイラによって機械語に変換される。
データ構造
データを効率的に格納・操作するための形式をデータ構造という。Rustの標準ライブラリには、Vec(ベクタ)、HashMap(ハッシュマップ)、BTreeMap(二分探索木マップ)、HashSet(ハッシュ集合)、LinkedList(連結リスト)などのデータ構造が提供されている。
テキストファイル
人間が読める文字のみで構成されたファイルをテキストファイルという。バイナリファイルと対比される概念である。
テスト
プログラムが仕様通りに動作するかを検証し、バグを発見する作業をテストという。Rustでは、#[test]属性を使用してテスト関数を定義し、cargo testコマンドで実行する。
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
#[test]
#[should_panic]
fn test_panic() {
panic!("テスト用のパニック");
}
}
デバッガ
プログラムのバグを発見・修正するための支援ツールをデバッガという。ブレークポイントの設定、ステップ実行、変数の監視などの機能を提供する。Rustでは、gdb、lldb、IDE内蔵のデバッガなどが使用される。
デバッグ
プログラムのバグを発見し、修正する作業をデバッグという。
トレイト
トレイトは、特定の型が持つべき機能を定義するRustの機能である。他の言語のインターフェースに相当する。トレイトはtraitキーワードで定義し、implキーワードで実装する。トレイトを使用することで、異なる型に共通の動作を定義し、ジェネリクスと組み合わせて柔軟なコードを記述できる。
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
title: String,
content: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{}: {}", self.title, self.content)
}
}
Rustの標準ライブラリには、Clone、Copy、Debug、Display、Dropなどの重要なトレイトが定義されている。
偽
Rustでは、ブール型のfalseのみが偽と評価される。他の言語とは異なり、0や空文字列は偽として評価されない。
if false {
println!("実行されない");
}
関連項目:真、true / false
分配束縛
タプルや構造体などの複合型から値を取り出して、複数の変数に同時に代入することを分配束縛(デストラクチャリング)という。分配束縛を使用することで、複合型のデータを簡潔に扱うことができる。
// タプルの分配
let (x, y, z) = (1, 2, 3);
// 構造体の分配
struct Point { x: i32, y: i32 }
let p = Point { x: 0, y: 7 };
let Point { x, y } = p;
// パターンマッチでの分配
match p {
Point { x: 0, y } => println!("y軸上の点: y = {}", y),
Point { x, y: 0 } => println!("x軸上の点: x = {}", x),
Point { x, y } => println!("それ以外: ({}, {})", x, y),
}
ブロック
複数の文をまとめた単位をブロックという。Rustでは、波括弧{}でブロックの範囲を示す。ブロックは式であり、最後の式の値を返す。
fn example() {
// ブロック開始
let x = 5;
let y = 10;
println!("{}", x + y);
// ブロック終了
}
// ブロックは式として使用できる
let result = {
let x = 3;
x + 1 // 4を返す
};
プログラム
コンピュータに実行させる命令の集まりをプログラムという。オペレーティングシステムなどのシステムプログラムと、特定の用途に使用するアプリケーションプログラムに分類される。
プログラミング
プログラムを設計・作成する作業をプログラミングという。
プログラミング言語
プログラムを記述するための人工言語をプログラミング言語という。Rust、Python、JavaScript、C、C++、Java、Go、Kotlinなどがある。
プログラミングスタイル
コードの書き方に関する規約やガイドラインをプログラミングスタイルという。Rustでは、rustfmtツールによる自動整形が標準であり、以下の慣例が使用されている。
- インデントには4つの空白を使用する
- 変数名、関数名はスネークケース(snake_case)
- 型名、トレイト名はパスカルケース(PascalCase)
- 定数名はアッパースネークケース(UPPER_SNAKE_CASE)
- 1行は99文字以内を推奨
プロセッサ
機械語のプログラムを解釈・実行するハードウェアをプロセッサ(CPU)という。
文
プログラムを構成する実行単位を文という。Rustでは、文は値を返さない。主な文の種類を以下に示す。
- let文:変数宣言
- 式文:式の後にセミコロンを付けたもの
Rustでは、if、loop、matchなどは文ではなく式である。
関連項目:式
変数
データを格納するための名前付きの領域を変数という。Rustでは、変数はletキーワードで宣言し、デフォルトでイミュータブルである。
// 不変変数
let x = 10;
// x = 11; // エラー
// 可変変数
let mut y = 20;
y = 21; // OK
// 型注釈
let z: i32 = 30;
// シャドーイング(同じ名前で新しい変数を作成)
let x = 5;
let x = x + 1; // 新しいxを作成
let x = "文字列"; // 型も変更可能
ベクタ
ベクタ(Vec<T>)は、動的にサイズが変更可能な配列である。ヒープに確保され、要素の追加や削除が可能である。要素数が不明または変化する場合に使用する。
// ベクタの作成
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
vec.push(3);
// vecマクロを使用
let vec = vec![1, 2, 3, 4, 5];
// 要素へのアクセス
println!("{}", vec[0]); // 1
println!("{:?}", vec.get(10)); // None(範囲外)
// イテレーション
for item in &vec {
println!("{}", item);
}
配列
同じ型の要素が固定サイズで並んだデータ構造を配列という。Rustの配列はスタックに確保され、サイズはコンパイル時に決定される。配列は固定サイズであるため、要素数が決まっている場合に使用する。
// 配列の作成
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
let zeros = [0; 100]; // 100個の0
// 要素へのアクセス
println!("{}", numbers[0]); // 1(インデックスは0から始まる)
// 範囲外アクセスはパニックを引き起こす
// let x = numbers[10]; // パニック
動的なサイズが必要な場合は、Vec型(ベクタ)を使用する。
ハノイの塔
ハノイの塔は、再帰的呼び出しの典型的な例題である。3本の柱と複数の円盤があり、「大きな円盤の上に小さな円盤のみ置ける」という制約のもと、円盤を1枚ずつ移動させて、すべての円盤を別の柱に移す問題である。
引数
関数呼び出し時に渡す値を引数(実引数)という。関数定義側で受け取る変数は仮引数(パラメータ)と呼ばれる。
評価
式からその値を計算することを評価という。
関連項目:式
標準出力
プログラムの出力先として既定で使用される出力チャネルを標準出力という。通常はディスプレイ(コンソール)に接続されている。Rustでは、println!マクロやprint!マクロが標準出力への出力を行う。
標準入力
プログラムの入力元として既定で使用される入力チャネルを標準入力という。通常はキーボードに接続されている。Rustでは、std::io::stdin()関数で標準入力にアクセスする。
use std::io;
let mut input = String::new();
io::stdin().read_line(&mut input).expect("入力の読み込みに失敗");
ファイル
ディスクなどの記憶装置に保存されるデータの単位をファイルという。ファイルの読み書きには、オープン(開く)、読み書き操作、クローズ(閉じる)という手順が必要である。
【Rustでのファイル操作】
use std::fs::File;
use std::io::{Read, Write};
// ファイルの読み込み
let mut file = File::open("input.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
// ファイルへの書き込み
let mut file = File::create("output.txt")?;
file.write_all(b"Hello, World!")?;
// より便利な関数
use std::fs;
let contents = fs::read_to_string("input.txt")?;
fs::write("output.txt", "Hello, World!")?;
ポインタ
メモリ上のアドレスを格納する変数をポインタという。C言語の概念であり、Rustでは参照やスマートポインタ(Box、Rc、Arcなど)を使用して、安全にメモリを操作する。
// 参照(安全なポインタ)
let x = 5;
let r = &x;
// Box(ヒープ割り当て)
let b = Box::new(5);
// Rc(参照カウント)
use std::rc::Rc;
let rc = Rc::new(5);
let rc2 = Rc::clone(&rc);
マクロ
マクロは、コードを生成するコードである。Rustでは、マクロは!記号で識別され、コンパイル時に展開される。マクロを使用することで、繰り返しの多いコードを簡潔に記述できる。
// 組み込みマクロ
println!("Hello, {}", name); // 書式付き出力
vec![1, 2, 3]; // ベクタ作成
panic!("エラー"); // パニック
// カスタムマクロの定義
macro_rules! say_hello {
() => {
println!("Hello!");
};
}
say_hello!();
ムーブ
所有権がある変数から別の変数に移動することをムーブという。ムーブ後、元の変数は使用できなくなる。これにより、二重解放やデータ競合を防ぐことができる。
let s1 = String::from("hello");
let s2 = s1; // s1からs2へ所有権がムーブ
// println!("{}", s1); // エラー:s1はもう使えない
println!("{}", s2); // OK
Copyトレイトを実装している型(整数、浮動小数点数、ブール値など)は、ムーブではなくコピーされる。
ミュータブル
変更可能な状態をミュータブルという。Rustでは、mutキーワードを付けることで変数をミュータブルにする。
let mut x = 5;
x = 6; // OK
let y = 10;
// y = 11; // エラー:イミュータブルな変数
モジュール
モジュールは、コードを整理し、名前空間を提供する仕組みである。modキーワードでモジュールを定義する。モジュールを使用することで、コードを論理的に分割し、可読性と保守性を向上させることができる。
mod my_module {
pub fn public_function() {
println!("公開関数");
}
fn private_function() {
println!("非公開関数");
}
}
// モジュールの使用
my_module::public_function();
文字型
1文字を格納するデータ型を文字型という。Rustではchar型が文字を表し、4バイトのUnicodeスカラ値である。
let c: char = 'z';
let emoji: char = '😀';
let japanese: char = 'あ';
文字列データ
文字の並びを表すデータを文字列という。Rustでは、&str型(文字列スライス)とString型(所有権を持つヒープ割り当て文字列)の2種類がある。
// 文字列スライス(不変)
let s1: &str = "Hello";
// String型(可変可能)
let mut s2: String = String::from("Hello");
s2.push_str(", world");
// 文字列操作
let length = s2.len();
let upper = s2.to_uppercase();
let combined = format!("{} {}", s1, s2);
メモリ
データやプログラムを一時的に記憶する装置をメモリという。各記憶位置にはアドレスが割り当てられている。Rustでは、メモリはスタックとヒープに分類され、所有権システムによって安全に管理される。
列挙型
複数の異なる値のバリアントを定義できるデータ型を列挙型という。Rustではenumキーワードで定義する。各バリアントはデータを保持でき、match式でパターンマッチングを行う。列挙型を使用することで、型安全な状態管理やエラー処理が可能になる。
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
let msg = Message::Write(String::from("Hello"));
match msg {
Message::Quit => println!("終了"),
Message::Write(text) => println!("{}", text),
_ => println!("その他のメッセージ"),
}
ライフタイム
ライフタイムは、参照が有効である期間を示すRustの概念である。Rustコンパイラは、すべての借用が有効なデータを指していることを保証するために、ライフタイムを使用する。ライフタイムにより、ダングリングポインタを防ぎ、メモリ安全性を保証する。
多くの場合、ライフタイムは暗黙的に推論されるが、複数の参照がある場合など、明示的なライフタイム注釈が必要になることがある。
// 明示的なライフタイム注釈
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
let string1 = String::from("long string");
let string2 = String::from("short");
let result = longest(string1.as_str(), string2.as_str());
ループ
処理を繰り返し実行することをループという。繰り返しを参照。
予約語
プログラミング言語において特別な意味を持ち、識別子として使用できない語を予約語(キーワード)という。Rustのキーワードは以下の通りである。
as async await break const continue crate dyn else enum extern false fn for if impl in let loop match mod move mut pub ref return Self self static struct super trait true type unsafe use where while 予約語(将来の使用のため): abstract become box do final macro override priv typeof unsized virtual yield
バイト
データの基本単位をバイトという。1バイトは8ビットで構成され、0から255までの256通りの値を表現できる。Rustでは、u8型が1バイトを表す。
Cargo
Cargoは、Rustの公式パッケージマネージャおよびビルドシステムである。プロジェクトの作成、依存関係の管理、ビルド、テストの実行などを行う。Cargoを使用することで、Rustプロジェクトの管理が統一的かつ効率的に行える。
cargo new my_project # 新規プロジェクト作成
cargo build # ビルド
cargo run # ビルドして実行
cargo test # テスト実行
cargo doc --open # ドキュメント生成
Copy
Copyトレイトを実装している型は、値がコピーされる(ムーブされない)。整数型、浮動小数点数型、ブール型、文字型など、スタックのみに格納される型はCopyを実装している。
let x = 5;
let y = x; // コピーされる
println!("{}, {}", x, y); // 両方とも使用可能
// Copyを実装していない型(String)
let s1 = String::from("hello");
let s2 = s1; // ムーブされる
// println!("{}", s1); // エラー
Option型
Option型は、値が存在するか否かを表す列挙型である。RustにはNULLポインタは存在しない代わりに、値の不在を型安全に表現するためにOption型を使用する。
enum Option<T> {
Some(T), // 値が存在
None, // 値が存在しない
}
let some_number = Some(5);
let no_number: Option<i32> = None;
// パターンマッチで処理
match some_number {
Some(n) => println!("値: {}", n),
None => println!("値なし"),
}
// unwrap_or でデフォルト値を指定
let value = no_number.unwrap_or(0);
Result型
Result型は、成功または失敗を表す列挙型である。回復可能なエラー処理に使用される。Result型を使用することで、エラーを型システムで表現し、安全に処理できる。
enum Result<T, E> {
Ok(T), // 成功時の値
Err(E), // エラー時の値
}
use std::fs::File;
// Result型を返す関数
let f = File::open("hello.txt");
match f {
Ok(file) => println!("ファイルを開きました"),
Err(error) => println!("エラー: {:?}", error),
}
// ?演算子でエラー伝播
fn read_file() -> Result<String, std::io::Error> {
let mut contents = String::new();
File::open("hello.txt")?.read_to_string(&mut contents)?;
Ok(contents)
}
スマートポインタ
スマートポインタは、ポインタのように動作するが、追加のメタデータと機能を持つデータ構造である。Rustの主なスマートポインタには以下がある。
- Box<T>:ヒープにデータを格納する
- Rc<T>:参照カウントによる複数の所有権
- Arc<T>:スレッドセーフな参照カウント
- RefCell<T>:実行時借用チェック
use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a);
let c = Rc::clone(&a);
println!("参照カウント: {}", Rc::strong_count(&a)); // 3
関連項目:ポインタ
実装
実装は、構造体や列挙型に対してメソッドや関連関数を定義することである。implキーワードを使用する。
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// メソッド(selfを取る)
fn area(&self) -> u32 {
self.width * self.height
}
// 関連関数(selfを取らない)
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
let rect = Rectangle { width: 30, height: 50 };
println!("面積: {}", rect.area());
let sq = Rectangle::square(10);
Rustの記号・キーワード等
本セクションでは、Rustで使用される記号とキーワードを解説する。
記号
!
論理否定演算子。ブール値を反転する。また、マクロ呼び出しの識別子としても使用される。
let x = true;
let y = !x; // false
println!("Hello"); // マクロ呼び出し
vec![1, 2, 3]; // マクロ呼び出し
!=
不等価比較演算子。2つのオペランドが等しくない場合にtrueを返す。
if x != 0 {
println!("xは0ではない");
}
#
属性の開始を示す記号。#[attribute]の形式で使用する。
#[derive(Debug)]
struct Point { x: i32, y: i32 }
#[test]
fn test_example() {
assert_eq!(2 + 2, 4);
}
%
剰余演算子。除算の余りを返す。
let remainder = 10 % 3; // 1
&
参照演算子、ビット単位のAND演算子。参照の作成に使用する。不変借用を表す。
let x = 5;
let r = &x; // 不変参照
let result = 5 & 3; // ビットAND: 1
使用例:複数の関数が同時に同じデータを読み取る場合に使用する。
fn print_length(s: &String) {
println!("{}", s.len());
}
let text = String::from("hello");
print_length(&text); // textの所有権は維持される
よくある誤用:不変参照と可変参照を同時に作成しようとするとコンパイルエラーになる。
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s; // エラー:不変借用が存在する間は可変借用できない
&&
論理積演算子。短絡評価を行う。
if x > 0 && y > 0 {
println!("両方とも正");
}
&mut
可変参照を作成する演算子。可変参照を通じて値の変更が可能だが、同時に存在できる可変参照は1つのみである。
let mut x = 5;
let y = &mut x;
*y += 1;
使用例:関数内でデータを変更する必要があるが、所有権は渡したくない場合に使用する。
fn append_world(s: &mut String) {
s.push_str(", world");
}
let mut text = String::from("hello");
append_world(&mut text); // textが変更される
よくある誤用:同時に複数の可変参照を作成しようとするとコンパイルエラーになる。
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // エラー:可変借用は同時に1つのみ
'(シングルクォート)
文字リテラルを囲む引用符。また、ライフタイムの注釈にも使用する。
let c: char = 'a';
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
()
let result = func(x, y); // 関数呼び出し
let t = (1, 2, 3); // タプル
let value = (a + b) * c; // グループ化
*
乗算演算子、参照外し演算子、rawポインタ宣言。
let product = 3 * 4; // 乗算: 12
let x = 5;
let r = &x;
let y = *r; // 参照外し: 5
+
加算演算子、トレイト境界の結合。
let sum = 3 + 4; // 加算: 7
// トレイト境界
fn notify(item: &(impl Display + Clone)) {
// 処理
}
-
減算演算子、単項マイナス演算子。
let diff = 10 - 3; // 減算: 7
let negative = -x; // 単項マイナス
->
fn add(a: i32, b: i32) -> i32 {
a + b
}
.
フィールドアクセス、メソッド呼び出し。
let len = vec.len(); // メソッド呼び出し
let name = person.name; // フィールドアクセス
..
範囲演算子。範囲の指定に使用する。
for i in 0..10 { // 0から9まで
println!("{}", i);
}
let slice = &array[1..4]; // インデックス1から3まで
/
除算演算子。
let quotient = 7 / 2; // 整数除算: 3
let quotient = 7.0 / 2.0; // 浮動小数点除算: 3.5
//
1行コメントの開始を示す記号。
// これはコメント
let x = 10; // 行末コメント
:
型注釈、トレイト境界の区切り。
let x: i32 = 10; // 型注釈
fn generic<T: Display>(item: T) { // トレイト境界
// 処理
}
::
パス区切り記号。モジュール、型、関連関数へのアクセスに使用する。
use std::collections::HashMap;
let v = Vec::new();
String::from("hello");
;
let x = 5; // 文
println!("Hello"); // 式文
let y = {
let x = 3;
x + 1 // 式(セミコロンなし):4を返す
};
let z = {
let x = 3;
x + 1; // 文(セミコロンあり):()を返す
};
<
「より小さい」を判定する比較演算子、ジェネリクスの型パラメータ開始。
if x < 100 {
println!("xは100未満");
}
let vec: Vec<i32> = Vec::new();
<<
左シフト演算子。
let result = 1 << 3; // 8(1を左に3ビットシフト)
<=
「以下」を判定する比較演算子。
if x <= 100 {
println!("xは100以下");
}
==
「等しい」を判定する比較演算子。
if x == 0 {
println!("xは0");
}
=>
match式のアーム、クロージャの本体を示す記号。
match x {
0 => println!("zero"),
_ => println!("other"),
}
let add = |a, b| a + b;
>
「より大きい」を判定する比較演算子、ジェネリクスの型パラメータ終了。
if x > 0 {
println!("xは正");
}
>=
「以上」を判定する比較演算子。
if x >= 0 {
println!("xは0以上");
}
>>
右シフト演算子。
let result = 8 >> 2; // 2(8を右に2ビットシフト)
=
代入演算子、パターンの束縛。
let x = 10;
?
エラー伝播演算子。Result型やOption型でエラーを呼び出し元に返す。?演算子を使用することで、エラー処理を簡潔に記述できる。
fn read_file() -> Result<String, std::io::Error> {
let mut contents = String::new();
File::open("hello.txt")?.read_to_string(&mut contents)?;
Ok(contents)
}
使用例:複数のエラーを返す可能性のある操作を連鎖させる場合に簡潔に記述できる。
fn process_file() -> Result<String, std::io::Error> {
let content = std::fs::read_to_string("input.txt")?;
std::fs::write("output.txt", &content)?;
Ok(content)
}
よくある誤用:?演算子はResultまたはOptionを返す関数内でのみ使用できる。
fn main() {
let content = std::fs::read_to_string("file.txt")?; // エラー:mainは()を返す
}
@
パターンマッチングでの束縛。値をテストしながら変数に束縛する。
match x {
n @ 1..=5 => println!("1から5の値: {}", n),
_ => println!("その他"),
}
[]
配列、ベクタのインデックスアクセス、配列リテラル、スライス。
let arr = [1, 2, 3, 4, 5]; // 配列リテラル
let item = arr[0]; // インデックスアクセス
let slice = &arr[1..3]; // スライス
\
文字列内でエスケープシーケンスの開始を示す。
let s = "Hello\nWorld"; // 改行
let path = "C:\\Users"; // バックスラッシュ
^
ビット単位のXOR演算子。
let result = 5 ^ 3; // 6(ビットXOR)
_
ワイルドカードパターン、使用しない変数の名前、数値リテラルの区切り。
let _ = some_function(); // 戻り値を無視
match x {
_ => println!("すべてにマッチ"),
}
let large = 1_000_000; // 数値の区切り
{}
ブロック、構造体リテラル、マクロのパターンに使用する括弧。
{
// ブロック
let x = 5;
}
let person = Person { name: "Alice", age: 30 }; // 構造体リテラル
|
ビット単位のOR演算子、クロージャのパラメータ区切り、パターンの選択。
let result = 5 | 3; // 7(ビットOR)
let add = |a, b| a + b; // クロージャ
match x {
1 | 2 | 3 => println!("1、2、または3"),
_ => println!("その他"),
}
||
論理和演算子。短絡評価を行う。
if x == 0 || y == 0 {
println!("どちらかがゼロ");
}
+= -= *= /= %=
複合代入演算子。演算と代入を同時に行う。
let mut x = 10;
x += 1; // x = x + 1 と同等
x *= 2; // x = x * 2 と同等
"(ダブルクォート)
文字列リテラルを囲む引用符。
let s = "Hello, world!";
英字(アルファベット順)
abs()
数値の絶対値を返すメソッド。整数型および浮動小数点数型で提供される。
let x = -10;
let y = x.abs(); // 10
let z = -3.14_f64;
let w = z.abs(); // 3.14
as
型変換(キャスト)、トレイト実装の明示、use時の別名指定に使用するキーワード。
let x = 5u32;
let y = x as f64; // 型変換
use std::io::Result as IoResult; // 別名
assert! / assert_eq!
条件が真であることを確認するマクロ。テストやデバッグに使用する。
assert!(x > 0, "xは正の数でなければならない");
assert_eq!(2 + 2, 4);
assert_ne!(x, y);
async / await
非同期処理のためのキーワード。asyncで非同期関数を定義し、awaitで非同期処理の完了を待つ。
async fn fetch_data() -> Result<String, Error> {
let response = http_get("url").await?;
Ok(response)
}
break
ループを中断するキーワード。値を返すこともできる。
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // 値を返してループ終了
}
};
const
定数を定義するキーワード。コンパイル時に評価される。
const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.141592653589793;
continue
ループの次の反復に移るキーワード。現在の反復の残りの処理をスキップする。
for i in 0..10 {
if i % 2 == 0 {
continue; // 偶数はスキップ
}
println!("{}", i);
}
crate
現在のクレートのルートを表すキーワード。パスの開始点として使用する。
use crate::my_module::my_function;
else
let number = if condition { 5 } else { 6 };
enum
列挙型を定義するキーワード。
enum Color {
Red,
Green,
Blue,
}
fn
関数を定義するキーワード。
fn add(a: i32, b: i32) -> i32 {
a + b
}
for
イテレータの要素を順に処理する繰り返し文のキーワード。
for item in collection {
println!("{}", item);
}
for i in 0..10 {
println!("{}", i);
}
if
条件分岐のキーワード。条件が真の場合に処理を実行する。Rustでは、if式は値を返す。
if x > 0 {
println!("正の数");
} else if x == 0 {
println!("ゼロ");
} else {
println!("負の数");
}
let number = if condition { 5 } else { 6 };
impl
実装を定義するキーワード。構造体や列挙型にメソッドを実装する。
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Display for Person {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
let
変数を宣言するキーワード。
let x = 5;
let mut y = 10;
let (a, b) = (1, 2);
使用例:変数はデフォルトで不変であり、意図しない変更を防ぐ。
let x = 5;
// x = 6; // エラー:不変変数に再代入できない
let mut y = 5;
y = 6; // OK:mutを付けると可変になる
よくある誤用:変数のシャドーイング(同名の新しい変数宣言)と再代入を混同する。
let x = 5;
let x = x + 1; // OK:シャドーイング(新しい変数)
let mut y = 5;
y = y + 1; // OK:再代入
loop
loop {
println!("無限ループ");
if condition {
break;
}
}
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // 値を返す
}
};
match
パターンマッチングのキーワード。値のパターンに基づいて分岐する。すべてのパターンを網羅する必要がある。matchを使用することで、すべてのケースを処理することが保証され、バグを減らすことができる。
match value {
0 => println!("zero"),
1 | 2 => println!("one or two"),
3..=9 => println!("three to nine"),
_ => println!("other"),
}
let result = match some_option {
Some(x) => x,
None => 0,
};
使用例:全てのケースを網羅する必要があるため、エラーハンドリングに最適である。
let result: Result<i32, &str> = Ok(42);
match result {
Ok(value) => println!("成功: {}", value),
Err(e) => println!("エラー: {}", e),
}
よくある誤用:全てのパターンを網羅しないとコンパイルエラーになる。
match some_option {
Some(x) => println!("{}", x),
// エラー:Noneケースが不足している
}
mod
モジュールを定義するキーワード。
mod my_module {
pub fn public_function() {
println!("公開関数");
}
}
move
クロージャがキャプチャした変数の所有権を取得するキーワード。スレッド間でデータを移動する際に必要となる。
let v = vec![1, 2, 3];
thread::spawn(move || {
println!("{:?}", v);
});
使用例:スレッドに変数を渡す場合、所有権を移動させる必要がある。
let data = vec![1, 2, 3];
thread::spawn(move || {
// dataの所有権がこのスレッドに移動
println!("{:?}", data);
});
// println!("{:?}", data); // エラー:dataの所有権は移動済み
よくある誤用:moveを付けずにスレッドで外部変数を使用するとコンパイルエラーになる。
let v = vec![1, 2, 3];
thread::spawn(|| {
println!("{:?}", v); // エラー:vの生存期間が不明
});
mut
let mut x = 5;
x = 6; // OK
let y = 10;
let r = &mut y; // 可変参照
panic!
回復不可能なエラーを発生させるマクロ。プログラムを即座に終了する。
if x < 0 {
panic!("xは負の数であってはならない");
}
println!
標準出力に改行付きで出力するマクロ。
println!("Hello, world!");
println!("{}は{}歳です", name, age);
println!("{:?}", vec); // デバッグ出力
pub
アイテムを公開するキーワード。他のモジュールからアクセス可能にする。
pub struct Point {
pub x: i32,
pub y: i32,
}
pub fn public_function() {
// 処理
}
return
関数から値を返すキーワード。最後の式はreturnなしで返すことができる。
fn add(a: i32, b: i32) -> i32 {
return a + b; // 明示的なreturn
}
fn multiply(a: i32, b: i32) -> i32 {
a * b // 最後の式が戻り値
}
self / Self
self:メソッドの第1パラメータで、インスタンス自身を表す。Self:現在の型を表す。
impl Rectangle {
fn area(&self) -> u32 { // selfはインスタンス
self.width * self.height
}
fn square(size: u32) -> Self { // Selfは型
Self {
width: size,
height: size,
}
}
}
static
静的変数を定義するキーワード。プログラムの実行期間中、固定されたメモリアドレスを持つ。
static HELLO: &str = "Hello, world!";
static mut COUNTER: u32 = 0; // 可変静的変数(unsafeが必要)
struct
構造体を定義するキーワード。
struct Person {
name: String,
age: u32,
}
trait
トレイトを定義するキーワード。
trait Summary {
fn summarize(&self) -> String;
}
true / false
let t = true;
let f = false;
type
型エイリアスを定義するキーワード。
type Kilometers = i32;
type Result<T> = std::result::Result<T, std::io::Error>;
unsafe
安全性チェックを無効化するキーワード。以下の操作に必要である。
- rawポインタの参照外し
- unsafe関数やメソッドの呼び出し
- 可変静的変数へのアクセスや変更
- unsafeトレイトの実装
unsafe {
let r = 0x012345usize as *const i32;
println!("{}", *r); // rawポインタの参照外し
}
use
パスをスコープに導入するキーワード。モジュールや型を簡潔に参照できる。
use std::collections::HashMap;
use std::io::{self, Write};
vec!
ベクタを作成するマクロ。
let v = vec![1, 2, 3, 4, 5];
let zeros = vec![0; 100]; // 100個の0
where
トレイト境界を指定するキーワード。複雑な境界を読みやすく記述できる。
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
// 処理
}
while
let mut count = 0;
while count < 10 {
println!("{}", count);
count += 1;
}