ちなみに人生初記事でっす。高校入るときのいい土産になったかも?
はじめに:なぜ「今」この記事が必要なのか
Rust で乱数を扱おうとして検索すると、多くの素晴らしい解説記事に出会います。しかし、そこで紹介されているコードを最新の環境で動かしようとすると、コンパイルエラーに直面することが少なくありません。その原因の多くは、Rust の主要な乱数クレートである rand のバージョンアップに伴う API の変更にあります。本記事では、初心者が「今」最短ルートで乱数を使いこなせるよう、最新バージョンに基づいた正しい書き方を解説します。
準備:30秒で終わるセットアップ
Rust で乱数を使うには、プロジェクトの設定ファイル(Cargo.toml)に依存関係を追加する必要があります。
[dependencies]
rand = "0.10.0"
この記事で紹介するコードはすべてこのバージョンで動作確認済みですので、安心して学習を進めてください。
【基本】まずはこれだけ:範囲指定で乱数を作る
ここ最近のバージョンアップで最も画期的なのは、乱数生成器(RNG)を明示的に準備することなく、一行で範囲指定の乱数を作れるようになった点(v0.9)です。
例えば「0 から 9 までの整数が欲しい」といった場合、専用の関数を呼び出すだけで済みます。
fn main() {
// rand 0.10.0 で推奨される 範囲付き乱数生成
// 0 から 9 までの整数をランダムに生成します
let n: u8 = rand::random_range(0..10);
println!("Random number (0-9): {}", n);
}
注目ポイント
-
let n: u8 = rand::random_range(0..10);- 解説: 1行で特定の範囲から乱数を取り出せます。非常にラクです。
この際、範囲の指定方法には 2 つの種類があります。
- 一つは「終端を含まない(
0..10)」指定。これは 0 から 9 までを生成します。 - もう一つは「終端を含む(
0..=10)」指定。これは 0 から 10 までを生成します。
2026.3.9追記:範囲指定で気を付けるポイント
コメントつけてくれた@scivola様に感謝
let n: u8 = rand::random_range(0..256);
とすると、
error: range endpoint is out of range for `u8`
--> src/main.rs:6:34
|
4 | let n: u8 = rand::random_range(0..256);
| ^^^^^^ help: use an inclusive range instead: `0..=255`
|
となります。u8の範囲を超えないように、
let n: u8 = rand::random_range(0..=255);
とする必要があります。
コード例
基本を理解したら、よく使うパターンを見ていきましょう。
そのいち:乱数生成器(RNG)を取得して使い回す
複数の乱数を続けて生成する場合やループ内で使用する場合は、一度生成器を取得するのが一般的です。
use rand::prelude::*;
fn main() {
// 1. RNG (乱数生成器) インスタンスを取得
let mut rng = rand::rng();
// 2. 生成器を使って複数の乱数を作る
let a: u32 = rng.random();
let b: f64 = rng.random_range(0.0..1.0);
println!("Random u32: {}", a);
println!("Random f64: {}", b);
// 3. ループ内での再利用
print!("Dice rolls: ");
for _ in 0..5 {
let die_roll = rng.random_range(1..=6);
print!("{} ", die_roll);
}
println!();
}
注目ポイント
-
let mut rng = rand::rng();-
解説: 現在のスレッドに紐付いた乱数生成器を取得します。内部状態を更新するため、必ず
mut修飾子が必要です。
-
解説: 現在のスレッドに紐付いた乱数生成器を取得します。内部状態を更新するため、必ず
-
let a: u32 = rng.random();-
解説: 指定した型の範囲全体(
u32なら 0〜42億強)から乱数を生成します。
-
解説: 指定した型の範囲全体(
-
let b: f64 = rng.random_range(0.0..1.0);- 解説: インスタンスメソッド版の範囲指定生成です。ループ内で何度も生成する場合はこちらの方が効率的です。
そのに:0.0〜1.0 の浮動小数点を生成する
ゲームの確率判定などでよく使われる手法です。
use rand::prelude::*;
fn main() {
let mut rng = rand::rng();
// 0.0 以上 1.0 未満の f64 乱数を生成
let f: f64 = rng.random();
println!("Random f64 (0.0-1.0): {}", f);
// 確率判定の例 (25% の確率で成功)
if f < 0.25 {
println!("Success! (25% chance)");
}
}
注目ポイント
-
let f: f64 = rng.random();-
解説:
f64型を指定して生成すると、自動的に 0.0 以上 1.0 未満の範囲の値が返されます。確率判定の王道パターンです。
-
解説:
そのさん:リストの中身をシャッフルする
トランプの配札や、データの順序をバラバラにしたい時に使います。
use rand::prelude::*;
fn main() {
let mut rng = rand::rng();
let mut numbers = vec![1, 2, 3, 4, 5];
// その場で要素をランダムに入れ替えます
numbers.shuffle(&mut rng);
println!("After shuffle: {:?}", numbers);
}
注目ポイント
-
numbers.shuffle(&mut rng);-
解説:
SliceRandomトレイトが提供するメソッドで、ベクタや配列の要素を直接(in-place)並べ替えます。
-
解説:
そのし:リストから要素を 1 つ選ぶ
「おみくじ」のように、リストの中からランダムに一つだけ中身を取り出す方法です。
use rand::prelude::*;
fn main() {
let mut rng = rand::rng();
let choices = ["Apple", "Banana", "Cherry", "Durian"];
// ランダムに1つの要素を安全に(Option型で)取得します
if let Some(choice) = choices.choose(&mut rng) {
println!("Today's snack: {}", choice);
}
}
注目ポイント
-
choices.choose(&mut rng)-
解説: リストからランダムに1つの要素への参照を返します。リストが空の場合を考慮して
Option<&T>型を返す安全な設計になっています。
-
解説: リストからランダムに1つの要素への参照を返します。リストが空の場合を考慮して
そのご:重みに基づいて選択する(ガチャの確率設定)
「レアアイテムは 1%、ノーマルアイテムは 85%」のように、特定の確率(重み)に従ってランダムに選択する方法です。
use rand::distr::{Distribution, weighted::WeightedIndex};
fn main() {
let mut rng = rand::rng();
let items = ["Normal", "Rare", "SuperRare"];
let weights = [85, 14, 1]; // 重みの比率
// 重みに基づいたインデックス生成器を作成
let dist = WeightedIndex::new(weights).unwrap();
// インデックス(0, 1, 2)を取得して対応するアイテムを表示
let idx = dist.sample(&mut rng);
println!("Result: {}", items[idx]);
}
注目ポイント
-
let dist = WeightedIndex::new(weights).unwrap();- 解説: 重みのリストから「インデックスの分布」を作成します。合計が 0 の場合などはエラーになるため、実戦では適切なエラー処理が推奨されます。
-
dist.sample(&mut rng);- 解説: 定義した重みに基づいて、インデックス番号(0, 1, 2...)を 1 つ取り出します。
そのろく:再現性が欲しい:シードを指定して固定する
デバッグ中や科学計算では、実行するたびに結果が変わると困る場合があります。特定の「シード値」を与えることで、常に同じ乱数の列を生成できます。
use rand::prelude::*;
use rand::rngs::StdRng;
fn main() {
let seed: u64 = 42;
// 特定のシードから RNG インスタンスを作成
let mut rng = StdRng::seed_from_u64(seed);
let n: u32 = rng.random();
println!("Seeded random: {}", n); // 常に同じ値が出力される
}
注目ポイント
-
StdRng::seed_from_u64(seed);- 解説: 予測可能な(固定された)シードから生成器を初期化します。同じシードを使えば、どの環境でもまったく同じ乱数列が再現されます。
落とし穴:初心者がハマる「よくある間違い」
学習中にエラーが出た際、以下のポイントをチェックしてみてください。
-
古い API の使用:
gen_rangeというメソッドは、古いバージョンでは主流でしたが、最新版ではrandom_range関数やメソッドに置き換わっています。 -
インポートの不足:
shuffleやchooseを使うには、use rand::prelude::*;を含めて必要なトレイトを読み込む必要があります。 -
状態の更新: 乱数生成器から値を取り出す際、生成器自身の内部状態が更新されます。そのため、生成器を保持する変数には「変更可能(
mut)」という印が必要です。
さらに高度な話題:ガウス分布
専門的な統計処理が必要な場合、平均と標準偏差を指定したサンプリング(正規分布)も可能です。この機能を利用するには、追加で rand_distr クレートを導入するのが一般的です。
[dependencies]
rand = "0.10.0"
rand_distr = "0.6.0"
use rand_distr::{Distribution, Normal};
fn main() {
let mut rng = rand::rng();
// 平均 0.0、標準偏差 1.0 のガウス分布を作成
let normal = Normal::new(0.0, 1.0).unwrap();
let v: f64 = normal.sample(&mut rng);
println!("Normal distribution sample: {:.4}", v);
}
注目ポイント
-
let normal = Normal::new(0.0, 1.0).unwrap();- 解説: 平均(mean)と標準偏差(std dev)を指定して分布を定義します。
-
normal.sample(&mut rng);- 解説: 定義した分布に従って値を 1 つサンプリングします。
まとめ
乱数の世界はめんどくさく奥が深く、より複雑な統計分布や、高いセキュリティが求められる暗号学的用途など、さらに高度なトピックも存在します。公式の「Rand Book」や、詳細なリファレンスである「Docs.rs」のページを覗いてみてください。
補足:rand crateはバージョンアップでどこが変わったのでしょう
2026.3.9追記:AIに主な変更点書かせてみた
ちょくちょく筆者にもわからん単語が飛んでます。
0.6.0 (2018-11-14)
機能追加と最適化
- SmallRng: アルゴリズムとして PCG を導入。
- シーケンス: WeightedIndex(重み付きサンプリング)の追加。
- SIMD: SIMD 型の生成サポートの開始。
0.7.0 (2019-06-28)
アーキテクチャの再設計と外部切り出し
- Edition 2018: Rust Edition 2018 への移行。
- OS インターフェースの分離: OS の乱数生成機能が getrandom クレートとして独立。
- 分布の外部化: 多くの特殊な分布が rand_distr クレートへ移動され、rand 本体は軽量化されました。
- StdRng: ChaCha アルゴリズムを採用。
0.8.0 (2020-12-18) (検索するとよく出てくる)
構文の改善とアルゴリズムの更新
- 範囲指定の変更: gen_range(a, b) が gen_range(a..b) のレンジ構文に変更されました。
- SmallRng の変更: アルゴリズムが PCG から xoshiro へ変更。
- Alphanumeric: 従来の char ではなく u8 (bytes) を生成するように変更。
- OS サポート: getrandom v0.2 へのアップデート。
0.9.0 (2025-01-27)
API の使いやすさ向上と 2024 Edition への準備
- トップレベル関数の刷新: thread_rng() が rng() に、Rng::gen() が random() に改称されました(Rust 2024 の gen
キーワードとの衝突回避のため)。 - 便利な関数の追加: random_range, random_bool, random_iter などのトップレベル関数が導入され、rng().random_range(1..10)
のように直感的に書けるようになりました。 - モジュール構造の整理: distributions モジュールが distr に短縮され、Standard 分布が StandardUniform に改称。
- フィーチャー名の変更: serde1 → serde、getrandom → os_rng。
0.10.0 (2026-02-08) - 最新
リネームとサポート環境のモダン化
- 依存関係の変更: rand_chacha から chacha20 へ移行(StdRng の実装が変更)。
- 用語の統一: choose_multiple が sample に、os_rng が sys_rng にリネームされました。
- トレイトの再定義: rand_core で RngCore が Rng に改称された影響で、rand の Rng トレイトは RngExt へリネームされました。
- Rust Edition 2024: Edition 2024 への移行と MSRV 1.85 への引き上げ。
- 追加: ジェネリックに RNG を作成する rand::make_rng() の導入。
- 削除: ReseedingRng および small_rng フィーチャーの削除。
最後に一つだけ。「必ず自分の手で動かして確認すること」を忘れずに!
Comments
範囲指定に関して
はできても
はできない,ということは一言あってもいいかもしれませんね。
(後者は「range endpoint is out of range for
u8」になる)コメントありがとうございます
Let's comment your feelings that are more than good