🦀
Rust言語のサンプルコード
文法解説で学んだ内容を使い、段階的に難易度が上がる3つの実践的なサンプルプログラムを紹介します。実際にRustのコンパイラ(rustcやCargo)で動かしながら読むと理解が深まります。
サンプル1:構造体とimplを使った在庫管理
structとimpl、Vec、所有権・借用の基本を組み合わせた小規模プログラムです。
addメソッドは&mut selfで自身を可変借用し、内部のVecに新しい要素を追加します。
totalメソッドは&selfで不変借用のみ行い、イテレータのmapとsumで合計を計算します。
SAMPLE PROGRAM
struct Item {
name: String,
qty: u32,
price: u32,
}
struct Inventory {
items: Vec- ,
}
impl Inventory {
fn new() -> Self {
Inventory { items: Vec::new() }
}
// &mut selfで自身を可変借用し、新しい商品を追加する
fn add(&mut self, name: &str, qty: u32, price: u32) {
self.items.push(Item {
name: name.to_string(),
qty,
price,
});
}
// 合計在庫数(&selfのみで計算できる読み取り専用処理)
fn total_qty(&self) -> u32 {
self.items.iter().map(|item| item.qty).sum()
}
// 在庫金額の合計
fn total_value(&self) -> u32 {
self.items.iter().map(|item| item.qty * item.price).sum()
}
// 在庫が少ない商品を探す(Option<&Item>を返す=見つからない可能性を型で表現)
fn find_low_stock(&self, threshold: u32) -> Option<&Item> {
self.items.iter().find(|item| item.qty < threshold)
}
}
fn main() {
let mut inv = Inventory::new();
inv.add("ノート", 10, 120);
inv.add("消しゴム", 3, 80);
inv.add("鉛筆", 25, 50);
println!("合計在庫数: {}", inv.total_qty());
println!("在庫金額合計: {}円", inv.total_value());
match inv.find_low_stock(5) {
Some(item) => println!("在庫少: {}(残り{}個)", item.name, item.qty),
None => println!("在庫が少ない商品はありません"),
}
}
サンプル2:クイックソートの実装(探索・整列アルゴリズム)
配列を「基準値(ピボット)より小さいグループ」と「大きいグループ」に分割し、それぞれを再帰的にソートするクイックソートを実装します。
平均計算量はO(n log n)ですが、ピボットの選び方が悪いと最悪計算量はO(n²)になる点も押さえておきましょう。
所有権の都合上、ここでは新しいVecを組み立てて返す関数型スタイルで実装しています。
SAMPLE PROGRAM
// クイックソート:平均計算量 O(n log n)、最悪計算量 O(n^2)
fn quick_sort(arr: &[i32]) -> Vec {
if arr.len() <= 1 {
return arr.to_vec(); // 要素が0か1個ならそのまま返す(再帰の終了条件)
}
let pivot = arr[arr.len() / 2]; // ピボットとして中央の要素を選ぶ
let less: Vec = arr.iter().filter(|&&x| x < pivot).copied().collect();
let equal: Vec = arr.iter().filter(|&&x| x == pivot).copied().collect();
let greater: Vec = arr.iter().filter(|&&x| x > pivot).copied().collect();
// less と greater をそれぞれ再帰的にソートしてから結合する
let mut result = quick_sort(&less);
result.extend(equal);
result.extend(quick_sort(&greater));
result
}
// 線形探索:計算量 O(n)。ソートされていない配列でも使える
fn linear_search(arr: &[i32], target: i32) -> Option {
for (i, &value) in arr.iter().enumerate() {
if value == target {
return Some(i);
}
}
None
}
fn main() {
let data = [5, 2, 9, 1, 5, 6, 3];
let sorted = quick_sort(&data);
println!("ソート結果: {:?}", sorted); // [1, 2, 3, 5, 5, 6, 9]
match linear_search(&data, 9) {
Some(idx) => println!("9はインデックス{}にあります", idx),
None => println!("見つかりませんでした"),
}
}
サンプル3:Result型を使ったエラーハンドリング(設定値の検証)
複数のバリデーション処理をResult<T, E>と?演算子でつなぎ、失敗した時点で即座にエラーを呼び出し元へ伝播させるパターンです。
独自のエラー型enum ConfigErrorを定義し、matchで種類ごとに異なるメッセージを表示します。例外機構を持たないRustならではの、型で表現するエラー処理の典型例です。
SAMPLE PROGRAM
#[derive(Debug)]
enum ConfigError {
OutOfRange(i32),
Empty,
}
struct Config {
name: String,
max_users: i32,
}
// 名前が空でないかを検証する
fn validate_name(name: &str) -> Result {
if name.trim().is_empty() {
return Err(ConfigError::Empty);
}
Ok(name.to_string())
}
// 値が範囲内かを検証する
fn validate_max_users(value: i32) -> Result {
if value < 1 || value > 1000 {
return Err(ConfigError::OutOfRange(value));
}
Ok(value)
}
// ?演算子で複数のResultを連結し、どこかで失敗したら即座にErrを返す
fn build_config(name: &str, max_users: i32) -> Result {
let name = validate_name(name)?;
let max_users = validate_max_users(max_users)?;
Ok(Config { name, max_users })
}
fn main() {
let inputs = [("サーバーA", 50), ("", 10), ("サーバーB", 5000)];
for (name, max_users) in inputs {
match build_config(name, max_users) {
Ok(config) => {
println!("OK: {} (max_users={})", config.name, config.max_users)
}
Err(ConfigError::Empty) => println!("エラー: 名前が空です"),
Err(ConfigError::OutOfRange(v)) => {
println!("エラー: max_usersが範囲外です(値: {})", v)
}
}
}
}