【Rust入門】STEP3:Rust特有の言語仕様について理解する

2023年5月18日木曜日

Rust

t f B! P L

enter image description here

Rustの基本を理解したら、次により詳細な概念について学びましょう。本節では、Rustの所有権と借用、エラー処理、そしてテストについて解説します。

所有権と借用

Rustの最も重要な特徴の一つは所有権と借用のシステムです。これはメモリ安全性を保証し、パフォーマンスを維持するためのものです。

所有権

Rustでは、各値は特定のownerという所有者を持ちます。所有者がスコープから外れると、その値は自動的にドロップされます。

{
  let s = "hello";   // sはこのスコープから有効です。
}   // このスコープが終了すると、sはドロップされます。

借用と参照

値を所有者から借りることもできます。これを借用といいます。借用は参照(&)を使って行います。

let s = String::from("hello");

let len = calculate_length(&s); // sの参照を関数に渡します。

fn calculate_length(s: &String) -> usize {
    s.len()
}

エラー処理

Rustでは、エラーは大きく2つのカテゴリに分類されます:回復可能なエラーと回復不可能なエラー。回復可能なエラーはResult型で、回復不可能なエラーはpanic!マクロで処理します。

Result型

Result型は、操作が成功した場合はOk値を、失敗した場合はErr値を返します。

use std::fs::File;

let f = File::open("file.txt");

let f = match f {
    Ok(file) => file,
    Err(error) => panic!("Problem opening the file: {:?}", error),
};

panic!マクロ

panic!マクロは、プログラムが何らかの理由で続行できないときに使用します。panic!を呼び出すと、プログラムはメッセージを表示し、スタックをクリーンアップして終了します。

panic!("crash and burn");

テスト

Rustには組み込みのテストフレームワークがあります。これを使用すると、コードが期待通りに動作することを確認するテストを簡単に作成できます。

fn add_two(a: i32) -> i32 {
    a + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_adds_two() {
        assert_eq!(4, add_two(2));
    }
}

上記のコードでは、add_two関数が正しく動作することをテストしています。テストは#[test]アノテーションを使用して定義し、assert_eq!マクロを使用して期待する結果を確認します。

これらの詳細な概念を理解することで、Rustでのプログラミングがより効率的になり、より安全なコードを書くことができます。次のステップでは、Rustでのプロジェクトの構築とパッケージ管理について学びましょう。

ジェネリクス

ジェネリクスは、多種多様なデータ型に対応するための機能です。ジェネリクスを使用すると、型に依存せずに柔軟なコードを書くことができます。

fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }

    largest
}

この関数は、PartialOrd(順序付け可能)とCopy(値がコピー可能)のトレイトを満たす任意の型Tに対応します。

トレイト

トレイトは、Rustの型が共有する必要がある振る舞いを定義する方法です。他の言語のインターフェースに似ています。

pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    // ...
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}", self.headline)
    }
}

この例では、Summaryトレイトと、それを実装するNewsArticle構造体を定義しています。

ライフタイム

ライフタイムは、Rustの所有権システムの一部で、参照が有効である期間をコンパイラに示すためのものです。

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

この関数は、2つの文字列スライスを取り、最も長いものへの参照を返します。'aはライフタイムアノテーションで、入力のライフタイムが出力のライフタイムと同じであることを示しています。

並行性

Rustでは、スレッドを使用して並行性を実現します。また、メッセージパッシングや共有状態を使用してスレッド間でデータを共有することができます。

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }

    handle.join().unwrap();
}

このコードは、新しいスレッドを生成(spawn)し、そのスレッドで一連のメッセージを印刷します。同時に、メインスレッドでも別のメッセージを印刷します。最後にjoinを使用してスポーンしたスレッドが終了するのを待ちます。

これらの高度な概念を理解することで、Rustでのプログラミングがさらに強力で効率的になります。次のステップでは、実際のプロジェクトに取り組んで、これらの概念を実践に活かしましょう。

スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ