Rust入門:基本文法とデータ型をマスター

Rustの基本文法をしっかりと理解することは、効率的で安全なコードを書くための第一歩です。

具体的には、変数宣言、ミュータビリティ、定数、データ型、コメント、命名規則などを取り上げます。

各項目には具体的なコード例を交え、初心者にも分かりやすく説明します。

変数とミュータビリティ

変数の宣言 (let)

Rustでは変数を宣言する際にletキーワードを使用します。

デフォルトでは、変数は不変(イミュータブル)です。

fn main() {
    let x = 5;
    println!("xの値は: {}", x);
}

ポイント:

  • letキーワードで変数xを宣言し、値5を代入。
  • デフォルトでxは不変であるため、後から値を変更することはできません。

ミュータブル変数 (let mut)

変数を変更可能にするには、mutキーワードを使用します。

fn main() {
    let mut y = 10;
    println!("yの初期値は: {}", y);
    y += 5;
    println!("yを5増やした後の値は: {}", y);
}

ポイント:

  • mutを付けることで、変数yの値を後から変更可能にします。
  • ミュータブル変数はプログラムの柔軟性を高めますが、使用には注意が必要です(後述する所有権や借用の概念と関連)。

変数のスコープ

Rustでは変数のスコープ(有効範囲)がブロック単位で決まります。

ブロックは中括弧 {} で囲まれた範囲です。

fn main() {
    let a = 10;
    {
        let b = 20;
        println!("ブロック内: a = {}, b = {}", a, b);
    }
    // println!("ブロック外: b = {}", b); // エラー: `b`はスコープ外
    println!("ブロック外: a = {}", a);
}

ポイント:

  • 変数bはブロック内でのみ有効で、ブロック外ではアクセスできません。
  • 変数amain関数全体で有効です。

定数と静的変数

定数 (const)

定数はプログラム全体で変更されることのない値を保持します。

constキーワードを使用して宣言し、型を必ず指定します。

const MAX_POINTS: u32 = 100_000;

fn main() {
    println!("最大ポイント数: {}", MAX_POINTS);
}

ポイント:

  • 定数名は一般的に全て大文字で、アンダースコアで区切ります。
  • 定数はグローバルスコープで宣言することが多いです。

静的変数 (static)

静的変数もプログラム全体で使用できる変数ですが、staticキーワードを使用します。

定数と異なり、静的変数はメモリ上に固定され、可変にすることも可能です(ただし、安全性のためにunsafeブロックが必要)。

static HELLO_WORLD: &str = "こんにちは、世界!";

fn main() {
    println!("{}", HELLO_WORLD);
}

ポイント:

  • static変数はプログラムの実行中ずっとメモリに存在します。
  • 可変な静的変数を宣言する場合は、static mutを使用し、アクセスにはunsafeブロックが必要です。

データ型の基本

Rustには主にスカラー型と複合型の2種類のデータ型があります。

それぞれの詳細について見ていきましょう。

スカラー型

スカラー型は単一の値を表します。Rustには以下の4つのスカラー型があります。

  1. 整数型 (i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize)
  2. 浮動小数点型 (f32, f64)
  3. ブール型 (bool)
  4. 文字型 (char)

整数型

整数型は符号付き(i)と符号なし(u)のものがあり、ビット数によって異なります。

fn main() {
    let a: i32 = -100;
    let b: u32 = 100;
    println!("a = {}, b = {}", a, b);
}

ポイント:

  • i32は-2,147,483,648から2,147,483,647までの整数を扱います。
  • u32は0から4,294,967,295までの整数を扱います。
  • isizeusizeはプラットフォームに依存するビット数(32ビットまたは64ビット)です。

浮動小数点型

浮動小数点型は小数を含む数値を扱います。

f32(単精度)とf64(倍精度)の2種類があります。

fn main() {
    let x: f32 = 3.14;
    let y: f64 = 2.71828;
    println!("x = {}, y = {}", x, y);
}

ポイント:

  • f64はより高い精度を持ち、デフォルトで使用されることが多いです。
  • 浮動小数点数は精度に限界があるため、厳密な計算には向きません。

ブール型

ブール型はtrueまたはfalseの2値を取ります。

fn main() {
    let t: bool = true;
    let f: bool = false;
    println!("t = {}, f = {}", t, f);
}

ポイント:

  • 条件分岐やループで頻繁に使用されます。

文字型

文字型は単一のUnicodeスカラ値を表します。

シングルクォートで囲まれた1文字を使用します。

fn main() {
    let c: char = 'z';
    let heart_eyed_cat: char = '😻';
    println!("c = {}, heart_eyed_cat = {}", c, heart_eyed_cat);
}

ポイント:

  • Rustのchar型は4バイトで、Unicodeの全ての文字を表現できます。

複合型

複合型は複数の値を一つにまとめて扱うことができます。

Rustでは主にタプル型と配列型が提供されています。

タプル型

タプル型は異なる型の値を一つにまとめることができます。

固定長で、各要素の型は異なっても構いません。

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (x, y, z) = tup;
    println!("x = {}, y = {}, z = {}", x, y, z);

    // インデックスを使ったアクセス
    let five_hundred = tup.0;
    let six_point_four = tup.1;
    let one = tup.2;
    println!("five_hundred = {}, six_point_four = {}, one = {}", five_hundred, six_point_four, one);
}

ポイント:

  • タプルの要素には型が異なるものを含めることができます。
  • デストラクチャリングを用いて、タプルの各要素を個別の変数に分解できます。
  • インデックスを使用して特定の要素にアクセスできます。

配列型

配列型は同じ型の値を固定長で格納します。

配列の長さは宣言時に決定され、その後変更することはできません。

fn main() {
    let a: [i32; 5] = [1, 2, 3, 4, 5];
    let first = a[0];
    let second = a[1];
    println!("first = {}, second = {}", first, second);

    // 配列の長さを取得
    println!("配列の長さは: {}", a.len());

    // 配列の繰り返し初期化
    let b = [3; 5]; // [3, 3, 3, 3, 3]
    println!("b = {:?}", b);
}

ポイント:

  • 配列の型は [型; 長さ] で表現します。
  • インデックスは0から始まり、範囲外アクセスはコンパイル時または実行時にエラーになります。
  • デバッグ用に{:?}フォーマットを使用して配列全体を表示できます。

定義と使用方法

型注釈の必要性

Rustは強い型付け言語であり、変数の型を明示的に指定することができます。

特にコンパイラが型を推論できない場合や、コードの可読性を高めるために型注釈を使用します。

fn main() {
    let guess: u32 = "42".parse().expect("Not a number!");
    println!("guess = {}", guess);
}

ポイント:

  • parseメソッドはジェネリックであり、型注釈によって期待する型を指定します。
  • 型注釈を用いることで、コンパイラが適切な型を推論できます。

型推論

Rustは強力な型推論を持ち、変数の初期化時に型を推論します。

型注釈が不要な場合も多いですが、必要に応じて明示的に指定することが可能です。

fn main() {
let x = 5; // i32として推論される
let y = 2.5; // f64として推論される
println!("x = {}, y = {}", x, y);
}


ポイント:

  • 初期化時に明確な値が与えられる場合、Rustはその値から型を推論します。
  • 型推論に頼りすぎず、必要に応じて明示的に型を指定することも重要です。

名前付け規則とコメント

名前付け規則

Rustでは変数名や関数名に関する特定の命名規則があります。

これに従うことで、コードの可読性と一貫性が向上します。

  • 変数名と関数名: スネークケース(小文字とアンダースコア)を使用します。
let user_age = 30;

fn calculate_area(radius: f64) -> f64 {
    3.14 * radius * radius
}

定数と静的変数: 全て大文字とアンダースコアを使用します。

const MAX_SPEED: u32 = 120;

static VERSION: &str = "1.0.0";

構造体と列挙型: キャメルケース(各単語の先頭を大文字)を使用します。

struct UserProfile {
    username: String,
    email: String,
}

enum Direction {
    North,
    South,
    East,
    West,
}

ポイント:

  • 一貫した命名規則は、コードの可読性と保守性を向上させます。
  • Rustコミュニティの慣習に従うことが推奨されます。

コメント

コメントはコードの理解を助け、後から見返す際の手がかりとなります。

Rustではシングルラインコメントとドキュメンテーションコメントがあります。

シングルラインコメント: // を使用します。

// これはシングルラインコメントです
let x = 10; // 変数xを宣言

ブロックコメント: /* */ を使用します。

/*
    これは
    ブロックコメントです
*/
let y = 20;

ドキュメンテーションコメント: /// または //! を使用します。

生成されるドキュメントに含まれます。

/// この関数は2つの数値を加算します
fn add(a: i32, b: i32) -> i32 {
    a + b
}

ポイント:

  • コメントは必要最低限に留め、コード自体が明確であることを目指します。
  • ドキュメンテーションコメントはAPIのドキュメント作成に有用です。

型キャストと型変換

Rustでは明示的な型変換が必要な場合があります。

asキーワードを使用して型キャストを行います。

fn main() {
    let decimal: f64 = 65.4321;
    let integer = decimal as u8;
    let character = integer as char;
    println!("integer = {}, character = {}", integer, character);
}

ポイント:

  • 浮動小数点数から整数型へのキャストは、小数点以下が切り捨てられます。
  • 整数型から文字型へのキャストは、ASCIIコードやUnicodeコードポイントに基づいて行われます。
  • 型変換時にはデータの損失やオーバーフローに注意が必要です。

スニペット:基本文法の実践

ここまで学んだ基本文法を活用して、簡単なプログラムを作成してみましょう。

ユーザー入力を受け取るプログラム

このプログラムはユーザーから名前を入力してもらい、挨拶を表示します。

use std::io;

fn main() {
    println!("名前を入力してください:");

    let mut name = String::new();

    io::stdin()
        .read_line(&mut name)
        .expect("入力の読み取りに失敗しました");

    println!("こんにちは、{}さん!", name.trim());
}

コード解説:

標準入力ライブラリのインポート

    use std::io;
    

    標準ライブラリのioモジュールを使用して、ユーザーからの入力を受け取ります。

    ユーザーへのプロンプト表示

    println!("名前を入力してください:");
    

    println!マクロを使用して、ユーザーに名前の入力を促します。

    変数の宣言と初期化

    let mut name = String::new();
    

    ミュータブルなString型の変数nameを宣言し、空の文字列で初期化します。

    ユーザー入力の読み取り

    io::stdin()
        .read_line(&mut name)
        .expect("入力の読み取りに失敗しました");
    

    標準入力からユーザーの入力を読み取り、name変数に格納します。

    エラーハンドリングとして、読み取りに失敗した場合はプログラムをパニックさせます。

    挨拶の表示

    println!("こんにちは、{}さん!", name.trim());

    入力された名前の前後の空白や改行を取り除くためにtrimメソッドを使用し、挨拶を表示します。

    ポイント

    • String::new()は新しい空のStringを生成します。
    • read_lineメソッドはユーザーの入力を受け取り、Stringに追加します。
    • expectメソッドはResult型を処理し、エラーが発生した場合にメッセージを表示します。
    • trimメソッドは文字列の前後の不要な空白を削除します。

    練習問題

    ここまで学んだ基本文法を確認するための練習問題をいくつか紹介します。

    練習問題1: 年齢を入力して判定するプログラム

    ユーザーから年齢を入力してもらい、その年齢が18歳以上かどうかを判定してメッセージを表示するプログラムを作成してください。

    ヒント:

    • 標準入力から文字列を読み取り、数値に変換する必要があります。
    • 条件分岐(if文)を使用します。

    簡単な計算機

    ユーザーから2つの数値を入力してもらい、その合計、差、積、商を計算して表示するプログラムを作成してください。

    ヒント:

    • 文字列から数値への変換を行います。
    • 複数のprintln!マクロを使用して結果を表示します。

    練習問題3: 配列の要素を合計するプログラム

    固定長の整数配列を定義し、その全ての要素の合計を計算して表示するプログラムを作成してください。

    ヒント:

    • forループを使用して配列をイテレートします。
    • 合計を保持する変数を用意します。

    まとめ

    このセクションでは、Rustの基本文法について詳しく学びました。以下は学んだ主なポイントのまとめです。

    • 変数宣言とミュータビリティ: letキーワードで変数を宣言し、mutを使用してミュータブルにする方法。
    • 定数と静的変数: conststaticの違いと使い方。
    • データ型: スカラー型(整数、浮動小数点、ブール、文字)と複合型(タプル、配列)の詳細。
    • 型注釈と型推論: Rustの強い型付けと型推論の仕組み。
    • 名前付け規則とコメント: コードの可読性を高めるための命名規則とコメントの使い方。
    • 型キャストと型変換: 異なるデータ型間の変換方法と注意点。
    • 実践的なコード例: ユーザー入力を受け取るプログラムを通じて基本文法を実践的に理解。

    次回予告

    次回の記事では、「制御構文と関数:Rustで効率的なコードを書く方法」を取り上げ、条件分岐やループ、関数の定義と使用方法について詳しく解説します。

    これにより、さらに複雑なロジックをRustで実装できるようになりますので、お楽しみに!