渚  直樹

渚 直樹

1635944400

Rustのエラーを処理する

エラー処理は、障害の可能性を予測して処理するプロセスです。

たとえば、プログラムがファイルの読み取りに失敗した後、その不正な入力を継続して使用すると、明らかに問題のあるエラーが発生します。これらのエラーに気づき、明示的に管理する機能により、プログラムをさまざまな追加の落とし穴から救うことができます。

学習目標

このモジュールでは、次の方法を学習します。

  • panic!回復不能なエラーを処理するために使用します。
  • Option値がオプションである場合、または値の欠如がエラー状態ではない場合は、列挙型を使用します。
  • Result問題が発生する可能性があり、発信者が問題に対処しなければならない可能性がある場合は、列挙型を使用してください。

前提条件

  • Rust開発環境
  • Cargoを使用したRustコードの作成、編集、および実行に関する知識

パニックによる致命的なエラーについて学びましょう!


パニックは、Rustで最も単純なエラー処理メカニズムです。

panic!マクロを使用して、現在のスレッドをパニックにすることができます。エラーメッセージを出力し、リソースを解放してから、プログラムを終了します。

この簡単な例は、panic!マクロを呼び出す方法を示しています。

fn main() {
    panic!("Farewell!");
}

このプログラムはステータスコード101で終了し、次のメッセージを出力します。

thread 'main' panicked at 'Farewell!', src/main.rs:2:5

前のパニックメッセージの最後の部分は、パニックの場所を示しています。これは、src /main.rsファイルの2行目の5番目の文字で発生しました。

一般的panic!に、プログラムが回復不能な状態に達したときに使用する必要があります。つまり、エラーから回復する方法がまったくない場合に使用します。

次のコードに示すように、ゼロ除算や、配列、ベクトル、またはハッシュマップに存在しないインデックスへのアクセスの試みなどの一部の操作でRustはパニックになります。

let v = vec![0, 1, 2, 3];
println!("{}", v[6]); // this will cause a panic!

次の単元では、プログラムをパニックに陥らせることなく、このようなエラーを処理する方法を学びます。

オプションタイプを使用して欠席に対処する

Rust標準ライブラリは、Option<T>値がない可能性がある場合に使用される列挙型を提供します。Option<T>Rustコードで広く使用されています。これは、存在する可能性のある値または空の可能性がある値を操作する場合に役立ちます。

他の多くの言語では、これはnullまたはを使用してモデル化されますnilが、Rustはnull他の言語と相互運用するコードの外部を使用しません。これは、Rustが値がオプションである場合について明示的であることを意味します。多くの言語でいる間、かかる機能Stringかもしれないが、実際に取るのいずれかStringまたはnull同じ機能のみを実際取ることができることを、錆で、String秒。Rustでオプションの文字列をモデル化する場合は、明示的に次のOptionタイプでラップする必要がありますOption<String>

Option<T> 次の2つのバリアントのいずれかとして現れます。

enum Option<T> {
    None,     // The value doesn't exist
    Some(T),  // The value exists
}

列挙型宣言の<T>一部は、Option<T>Tが汎用でありSomeOption列挙型のバリアントに関連付けられることを示しています。

前のセクションで説明したようにNoneSomeはタイプではなく、Option<T>タイプのバリアントです。これは、とりわけ、関数が引数として、SomeまたはNone引数として取ることができず、のみであることを意味しますOption<T>

前の単元で、ベクトルの存在しないインデックスにアクセスしようとするとプログラムがにアクセスするpanicことを説明しましVec::getたが、Optionパニックの代わりに型を返すメソッドを使用することでそれを回避できます。指定されたインデックスに値が存在する場合、その値はOption::Some(value)バリアントにラップされます。インデックスが範囲外の場合、Option::None代わりに値を返します。
 

やるだけやってみよう。以下のコードは、ローカルまたはRustプレイグラウンドで実行できます。

let fruits = vec!["banana", "apple", "coconut", "orange", "strawberry"];

// pick the first item:
let first = fruits.get(0);
println!("{:?}", first);

// pick the third item:
let third = fruits.get(2);
println!("{:?}", third);

// pick the 99th item, which is non-existent:
let non_existent = fruits.get(99);
println!("{:?}", non_existent);

出力は次のとおりです。

Some("banana")
Some("coconut")
None

印刷されたメッセージは、fruits配列内の既存のインデックスにアクセスしようとした最初の2回の試行でとが発生したことを示していますが、99番目の要素をフェッチしようとするSome("banana")Some("coconut")Noneパニックになる代わりに値(データに関連付けられていない)が返されました。

実際には、取得する列挙型バリアントに応じて、プログラムの動作を決定する必要があります。しかし、Some(data)バリアント内のデータにアクセスするにはどうすればよいでしょうか。

パターンマッチング

Rustには、と呼ばれる強力な演算子がありmatchます。パターンを提供することにより、プログラムのフローを制御するために使用できます。ときにmatch一致するパターンを見つけ、それはあなたがそのパターンで供給することにコードを実行します。

let fruits = vec!["banana", "apple", "coconut", "orange", "strawberry"];
for &index in [0, 2, 99].iter() {
    match fruits.get(index) {
        Some(fruit_name) => println!("It's a delicious {}!", fruit_name),
        None => println!("There is no fruit! :("),
    }
}

この例をRustプレイグラウンドで実行してみることができます。

出力は次のとおりです。

It's a delicious banana!
It's a delicious coconut!
There is no fruit! :(

前のコードでは、前の例(0、2、および99)と同じインデックスを反復処理し、それぞれを使用fruitsして、fruits.get(index)式を使用してベクトルから値を取得します。

のでfruits、ベクターに含まれる&str要素を、私たちは、この式の結果は型であることを知っていますOption<&str>。次に、値に対して一致式を使用し、Optionそのバリアントごとに一連のアクションを定義します。Rustは、これらのブランチを一致アームと呼び、各アームは、一致した値に対して1つの可能な結果を処理できます。

最初のアームは、新しい変数、を導入しますfruit_name。この変数は、値内の任意の値と一致しSomeます。のスコープは一致式にfruit_name限定されているため、でfruit_name導入する前に宣言することは意味がありませんmatch。

バリアント内の値に応じて、一致式をさらに改良して、異なる動作をすることができますSome。たとえば、次のコマンドを実行することで、ココナッツが素晴らしいという事実を強調できます。

let fruits = vec!["banana", "apple", "coconut", "orange", "strawberry"];
for &index in [0, 2, 99].iter() {
    match fruits.get(index) {
        Some(&"coconut") => println!("Coconuts are awesome!!!"),
        Some(fruit_name) => println!("It's a delicious {}!", fruit_name),
        None => println!("There is no fruit! :("),
    }
}

ノート

一致する最初のパターンはSome(&"coconut")&文字列リテラルの前に注意してください)です。これは、文字列スライスへの参照のまたはオプションをfruits.get(index)返すためOption<&&str>です。&パターンから削除するということは、Option<&str>(文字列スライスへのオプションの参照ではなく、オプションの文字列スライス)と照合しようとしていることを意味します。参照については説明していませんので、現時点では完全には意味がない可能性があります。今のところ&、タイプが正しく並んでいることを確認していることを覚えておいてください。

この例をRustプレイグラウンドで実行してみることができます。

出力は次のとおりです。

It's a delicious banana!
Coconuts are awesome!!!
There is no fruit! :(

文字列値が"coconut"、の場合、最初のアームが照合され、実行のフローを決定するために使用されることに注意してください。

一致式を使用するときは常に、次のルールに注意してください。

  • match腕は上から下に評価されます。特定のケースは、一般的なケースよりも早く定義する必要があります。そうしないと、一致して評価されることはありません。
  • matchアームは、入力タイプが持つ可能性のあるすべての可能な値をカバーする必要があります。網羅的でないパターンリストと照合しようとすると、コンパイラエラーが発生します。

場合のlet表現

Rustは、値が単一のパターンに準拠しているかどうかをテストするための便利な方法を提供します。

次の例では、への入力matchOption<u8>値です。matchその入力値がある場合式はコードのみを実行する必要があります7

let a_number: Option<u8> = Some(7);
match a_number {
    Some(7) => println!("That's my lucky number!"),
    _ => {},
}

この場合、NoneバリアントとSome<u8>一致しないすべての値を無視しますSome(7)。ワイルドカードパターンは、このタイプの状況に役立ちます。あなたは追加することができ_、他の全てのパターンが一致した後(アンダースコア)のワイルドカードパターンを何かをし、マッチ腕を排出するためのコンパイラの需要を満たすために使われています。

このコードを要約するには、iflet式を使用できます。

let a_number: Option<u8> = Some(7);
if let Some(7) = a_number {
    println!("That's my lucky number!");
}

場合のlet演算子は式のパターンを比較します。式がパターンに一致する場合、ifブロックが実行されます。if let式の良いところは、一致する単一のパターンに関心がある場合、一致式の定型コードをすべて必要としないことです。

使用unwrapしてexpect

メソッドOptionを使用して、型の内部値に直接アクセスすることを試みることができますunwrap。ただし、バリアントがNone。の場合、このメソッドはパニックになるので注意してください。

例えば:

let gift = Some("candy");
assert_eq!(gift.unwrap(), "candy");

let empty_gift: Option<&str> = None;
assert_eq!(empty_gift.unwrap(), "candy"); // This will panic!

この場合、コードは次の出力でパニックになります。

    thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:6:27

このexpectメソッドはと同じようunwrapに機能しますが、2番目の引数によって提供されるカスタムパニックメッセージを提供します。

let a = Some("value");
assert_eq!(a.expect("fruits are healthy"), "value");

let b: Option<&str> = None;
b.expect("fruits are healthy"); // panics with `fruits are healthy`

出力は次のとおりです。

    thread 'main' panicked at 'fruits are healthy', src/main.rs:6:7

これらの関数はパニックになる可能性があるため、使用することはお勧めしません。代わりに、次のいずれかのアプローチを検討してください。

  • パターンマッチングを使用して、Noneケースを明示的に処理します。
  • のような同様のパニックにならないメソッドを呼び出しunwrap_orます。これは、バリアントがの場合はデフォルト値を返し、バリアントがのNone場合は内部値を返しますSome(value)
assert_eq!(Some("dog").unwrap_or("cat"), "dog");
assert_eq!(None.unwrap_or("cat"), "cat");

演習-不在に対処するためにオプションタイプを使用する


この演習では、Person構造体を受け取り、Stringそのフルネームを含むを返す関数の実装を終了します。

ミドルネームを持っていない人もいますが、持っている場合は、戻り値に含める必要があることに注意してください。

build_full_name関数のみを編集する必要があります。名と姓を処理する部分はすでに実装されていることに注意してください。

struct Person {
    first: String,
    middle: Option<String>,
    last: String,
}

fn build_full_name(person: &Person) -> String {
    let mut full_name = String::new();
    full_name.push_str(&person.first);
    full_name.push_str(" ");

    // TODO: Implement the part of this function that handles the person's middle name.

    full_name.push_str(&person.last);
    full_name
}

fn main() {
    let john = Person {
        first: String::from("James"),
        middle: Some(String::from("Oliver")),
        last: String::from("Smith"),
    };
    assert_eq!(build_full_name(&john), "James Oliver Smith");

    let alice = Person {
        first: String::from("Alice"),
        middle: None,
        last: String::from("Stevens"),
    };
    assert_eq!(build_full_name(&alice), "Alice Stevens");

    let bob = Person {
        first: String::from("Robert"),
        middle: Some(String::from("Murdock")),
        last: String::from("Jones"),
    };
    assert_eq!(build_full_name(&bob), "Robert Murdock Jones");
}

上記のコードを実行し、すべてのassert_eq!式がパニックにならずに合格することを確認します。Rustプレイグラウンドでコードを編集することもできます。

結果タイプを使用してエラーを処理します

Rustは、Result<T, E>エラーを返したり伝播したりするための列挙型を提供します。慣例により、Ok(T)バリアントは成功を表し、値を含み、バリアントErr(E)はエラーを表し、エラー値を含みます。

Result<T, E>列挙型は次のように定義されています。

enum Result<T, E> {
    Ok(T):  // A value T was obtained.
    Err(E): // An error of type E was encountered instead.
}

Optionない可能性を説明するタイプとは対照的に、タイプResult障害が発生する可能性がある場合に最適です。

このResultタイプには、次のいずれかを実行するunwrapandexpectメソッドもあります。

  • Okこの場合、バリアント内の値を返します。
  • バリアントがErr。の場合、プログラムをパニックにします。

Result実際の動作を見てみましょう。次のサンプルコードには、次のsafe_divisionいずれかを返す関数の実装があります。

  • 成功した除算の結果を運ぶバリアントを持つResultOk
  • Err構造体担持バリアントDivisionByZeroError失敗分割信号を送ります。
#[derive(Debug)]
struct DivisionByZeroError;

fn safe_division(dividend: f64, divisor: f64) -> Result<f64, DivisionByZeroError> {
    if divisor == 0.0 {
        Err(DivisionByZeroError)
    } else {
        Ok(dividend / divisor)
    }
}

fn main() {
    println!("{:?}", safe_division(9.0, 3.0));
    println!("{:?}", safe_division(4.0, 0.0));
    println!("{:?}", safe_division(0.0, 2.0));
}

このプログラムを確認するには、Rustプレイグラウンドにアクセスしてください。

出力は次のとおりです。

Ok(3.0)
Err(DivisionByZeroError)
Ok(0.0)

構造体の#[derive(Debug)]前の部分DivisionByZeroErrorは、デバッグ目的で型を印刷可能にするようにRustコンパイラーに指示するマクロです。この概念については、後の特性モジュールで詳しく説明します。
演習-結果タイプを使用してエラーを処理します

この演習では、コンピューターからファイルを読み取るプログラムにエラー処理を追加します。サンプルプログラムでは、read_file_contents関数はPathBuf単一の入力として構造体を受け取り、を返しますResult<String, io::Error>。この関数は次のタスクを実行します。

  1. 変更可能な空のString変数を作成します。
  2. 指定されたパスにあるファイルにアクセスします。
  3. メソッドを使用して、ファイルの内容をString変数に読み込みますread_to_string
  4. 変更されたString変数を返します。

コードの詳細は次のとおりです。

  • プログラムはいくつかの構造体と特性を使用します。コードの最初の3行は、これらのデータ型をスコープに入れます。
  • 一部のmatchアームは、file_handleやのような変数を導入しますio_error。それらのスコープはmatch式に制限されているため、前のコードでは宣言されていませんmatch
  • このopenメソッドはResult<File, Error>列挙型を返します。エラーが発生しない場合は、Okバリアントでラップされたファイルハンドルを返します。
  • このread_to_stringメソッドは、ファイルの内容をstring戻り値ではなく、渡されたパラメーターに追加します。
  • プログラムは、match式を使用して変数に値を割り当てます。その代入ステートメント内で、matcharmsを使用して関数から早期に戻ることもできます。

サンプルプログラムを開く

この演習のサンプルコードで作業するには、次の2つのオプションがあります。

  • 次のコードをコピーして、ローカル開発環境で編集します。
  • この準備されたRustPlaygroundでコードを開きます。

ローカル開発環境で編集する

ローカルコンピューターでこのコードを実行するには、src /main.rsファイルにコードを記述します。ファイルはCargoプロジェクトのルートにある必要があります。新しいCargoプロジェクトを設定する方法に関する質問については、この学習パスの最初のモジュールを確認してください。

さび遊び場で働く

Rust Playgroundで演習を完了するには、独自のコードをsrc /main.rsという名前のファイルとして読み取ることができます。このファイルは独自の仮想パス上にあります。

use std::fs::File;
use std::io::{Error, Read};
use std::path::PathBuf;

fn read_file_contents(path: PathBuf) -> Result<String, Error> {
    let mut string = String::new();

    // Access a file at a specified path
    // ---------------------------------
    // TODO #1:
    // - Pass variable to `file` variable on success, or
    // - Return from function early if there's an error
    let mut file: File = match File::open(path) {
        Ok(file_handle) => todo!("Pass variable to `file` variable on success"),
        Err(io_error) => todo!("Return from function early if there's an error")
    };

    // Read file contents into `String` variable with `read_to_string`
    // ---------------------------------
    // Success path is already filled in
    // TODO #2: Return from function early if there's an error
    match file.read_to_string(&mut string) {
        Ok(_) => (),
        Err(io_error) => todo!("Return from function early if there's an error")
    };

    // TODO #3: Return `string` variable as expected by function signature
    todo!("Return `string` variable")
}

fn main() {
    if read_file_contents(PathBuf::from("src/main.rs")).is_ok() {
        println!("The program found the main file.");
    }
    if read_file_contents(PathBuf::from("non-existent-file.txt")).is_err() {
        println!("The program reported an error for the file that doesn't exist.");
    }
}

成功と失敗のシナリオを処理する

最初のタスクは、成功と失敗のシナリオを処理するためのコードを追加することです。

ノート

サンプルコードで、TODOコメントとtodo!マクロを探します。コメントは、完了するタスクを説明しています。このマクロは、終了または更新する必要のあるコードを示します。

次のコードを更新して、match式内の成功と失敗のシナリオを処理します。

    // Access a file at a specified path
    // ---------------------------------
    // TODO #1:
    // - Pass variable to `file` variable on success, or
    // - Return from function early if there's an error
    let mut file: File = match File::open(path) {
        Ok(file_handle) => todo!("Pass variable to `file` variable on success"),
        Err(io_error) => todo!("Return from function early if there's an error")
    };

タスクを完了したら、次のプログラミング目標に取り組みます。

  • Ok(value)ケースは、内側を提供する必要がありますvalue
  • Err(error_value)値は早期から返却されなければならないread_file_contents機能。

エラーシナリオを処理する

次のタスクは、エラー処理を追加することです。Errケースをサポートするために、次のコードを更新してください。

    // Read file contents into `String` variable with `read_to_string`
    // ---------------------------------
    // Success path is already filled in
    // TODO #2: Return from function early if there's an error
    match file.read_to_string(&mut string) {
        Ok(_) => (),
        Err(io_error) => todo!("Return from function early if there's an error")
    };

このタスクに取り組むときは、次のプログラミング目標に取り組んでください。

  • Ok(value)ケースは、内側を提供する必要がありますvalue
  • Err(error_value)値は早期から返却されなければならないread_file_contents機能。
     

文字列を返す

最後のタスクは、コードを修正しStringて、Okバリアント内の変更された変数を返すことです。実装は、戻り値が関数の期待される成功した出力であることを表現する必要があります。

fn read_file_contents(path: PathBuf) -> Result<String, Error> {
    ...
    // TODO #3: Return `string` variable as expected by function signature
    todo!("Return `string` variable")
}

プログラムをビルドする

タスクを完了したら、プログラムをビルドして実行します。次の出力が表示されます。

The program found the main file.
The program reported an error for the file that doesn't exist.

概要

このモジュールでは、次のことを学びました。

  • を使用して、回復不能なエラーが発生した場合にRustプログラムを停止する方法panic!
  • 列挙型を使用して値が存在しない可能性を表す方法Option
  • Result列挙型を使用して操作を表す方法。
  • パターンマッチングを使用して、特定のタイプのすべての可能な値に安全にアクセスする方法。

この学習パスの次のモジュールでは、Rustの最も斬新な側面の1つであるボローチェッカーと、コード内のさまざまな種類のバグを防ぐ方法について学習します。

 リンク: https://docs.microsoft.com/en-us/learn/modules/rust-error-handling/

#rust #Beginners 

What is GEEK

Buddha Community

Rustのエラーを処理する
渚  直樹

渚 直樹

1635944400

Rustのエラーを処理する

エラー処理は、障害の可能性を予測して処理するプロセスです。

たとえば、プログラムがファイルの読み取りに失敗した後、その不正な入力を継続して使用すると、明らかに問題のあるエラーが発生します。これらのエラーに気づき、明示的に管理する機能により、プログラムをさまざまな追加の落とし穴から救うことができます。

学習目標

このモジュールでは、次の方法を学習します。

  • panic!回復不能なエラーを処理するために使用します。
  • Option値がオプションである場合、または値の欠如がエラー状態ではない場合は、列挙型を使用します。
  • Result問題が発生する可能性があり、発信者が問題に対処しなければならない可能性がある場合は、列挙型を使用してください。

前提条件

  • Rust開発環境
  • Cargoを使用したRustコードの作成、編集、および実行に関する知識

パニックによる致命的なエラーについて学びましょう!


パニックは、Rustで最も単純なエラー処理メカニズムです。

panic!マクロを使用して、現在のスレッドをパニックにすることができます。エラーメッセージを出力し、リソースを解放してから、プログラムを終了します。

この簡単な例は、panic!マクロを呼び出す方法を示しています。

fn main() {
    panic!("Farewell!");
}

このプログラムはステータスコード101で終了し、次のメッセージを出力します。

thread 'main' panicked at 'Farewell!', src/main.rs:2:5

前のパニックメッセージの最後の部分は、パニックの場所を示しています。これは、src /main.rsファイルの2行目の5番目の文字で発生しました。

一般的panic!に、プログラムが回復不能な状態に達したときに使用する必要があります。つまり、エラーから回復する方法がまったくない場合に使用します。

次のコードに示すように、ゼロ除算や、配列、ベクトル、またはハッシュマップに存在しないインデックスへのアクセスの試みなどの一部の操作でRustはパニックになります。

let v = vec![0, 1, 2, 3];
println!("{}", v[6]); // this will cause a panic!

次の単元では、プログラムをパニックに陥らせることなく、このようなエラーを処理する方法を学びます。

オプションタイプを使用して欠席に対処する

Rust標準ライブラリは、Option<T>値がない可能性がある場合に使用される列挙型を提供します。Option<T>Rustコードで広く使用されています。これは、存在する可能性のある値または空の可能性がある値を操作する場合に役立ちます。

他の多くの言語では、これはnullまたはを使用してモデル化されますnilが、Rustはnull他の言語と相互運用するコードの外部を使用しません。これは、Rustが値がオプションである場合について明示的であることを意味します。多くの言語でいる間、かかる機能Stringかもしれないが、実際に取るのいずれかStringまたはnull同じ機能のみを実際取ることができることを、錆で、String秒。Rustでオプションの文字列をモデル化する場合は、明示的に次のOptionタイプでラップする必要がありますOption<String>

Option<T> 次の2つのバリアントのいずれかとして現れます。

enum Option<T> {
    None,     // The value doesn't exist
    Some(T),  // The value exists
}

列挙型宣言の<T>一部は、Option<T>Tが汎用でありSomeOption列挙型のバリアントに関連付けられることを示しています。

前のセクションで説明したようにNoneSomeはタイプではなく、Option<T>タイプのバリアントです。これは、とりわけ、関数が引数として、SomeまたはNone引数として取ることができず、のみであることを意味しますOption<T>

前の単元で、ベクトルの存在しないインデックスにアクセスしようとするとプログラムがにアクセスするpanicことを説明しましVec::getたが、Optionパニックの代わりに型を返すメソッドを使用することでそれを回避できます。指定されたインデックスに値が存在する場合、その値はOption::Some(value)バリアントにラップされます。インデックスが範囲外の場合、Option::None代わりに値を返します。
 

やるだけやってみよう。以下のコードは、ローカルまたはRustプレイグラウンドで実行できます。

let fruits = vec!["banana", "apple", "coconut", "orange", "strawberry"];

// pick the first item:
let first = fruits.get(0);
println!("{:?}", first);

// pick the third item:
let third = fruits.get(2);
println!("{:?}", third);

// pick the 99th item, which is non-existent:
let non_existent = fruits.get(99);
println!("{:?}", non_existent);

出力は次のとおりです。

Some("banana")
Some("coconut")
None

印刷されたメッセージは、fruits配列内の既存のインデックスにアクセスしようとした最初の2回の試行でとが発生したことを示していますが、99番目の要素をフェッチしようとするSome("banana")Some("coconut")Noneパニックになる代わりに値(データに関連付けられていない)が返されました。

実際には、取得する列挙型バリアントに応じて、プログラムの動作を決定する必要があります。しかし、Some(data)バリアント内のデータにアクセスするにはどうすればよいでしょうか。

パターンマッチング

Rustには、と呼ばれる強力な演算子がありmatchます。パターンを提供することにより、プログラムのフローを制御するために使用できます。ときにmatch一致するパターンを見つけ、それはあなたがそのパターンで供給することにコードを実行します。

let fruits = vec!["banana", "apple", "coconut", "orange", "strawberry"];
for &index in [0, 2, 99].iter() {
    match fruits.get(index) {
        Some(fruit_name) => println!("It's a delicious {}!", fruit_name),
        None => println!("There is no fruit! :("),
    }
}

この例をRustプレイグラウンドで実行してみることができます。

出力は次のとおりです。

It's a delicious banana!
It's a delicious coconut!
There is no fruit! :(

前のコードでは、前の例(0、2、および99)と同じインデックスを反復処理し、それぞれを使用fruitsして、fruits.get(index)式を使用してベクトルから値を取得します。

のでfruits、ベクターに含まれる&str要素を、私たちは、この式の結果は型であることを知っていますOption<&str>。次に、値に対して一致式を使用し、Optionそのバリアントごとに一連のアクションを定義します。Rustは、これらのブランチを一致アームと呼び、各アームは、一致した値に対して1つの可能な結果を処理できます。

最初のアームは、新しい変数、を導入しますfruit_name。この変数は、値内の任意の値と一致しSomeます。のスコープは一致式にfruit_name限定されているため、でfruit_name導入する前に宣言することは意味がありませんmatch。

バリアント内の値に応じて、一致式をさらに改良して、異なる動作をすることができますSome。たとえば、次のコマンドを実行することで、ココナッツが素晴らしいという事実を強調できます。

let fruits = vec!["banana", "apple", "coconut", "orange", "strawberry"];
for &index in [0, 2, 99].iter() {
    match fruits.get(index) {
        Some(&"coconut") => println!("Coconuts are awesome!!!"),
        Some(fruit_name) => println!("It's a delicious {}!", fruit_name),
        None => println!("There is no fruit! :("),
    }
}

ノート

一致する最初のパターンはSome(&"coconut")&文字列リテラルの前に注意してください)です。これは、文字列スライスへの参照のまたはオプションをfruits.get(index)返すためOption<&&str>です。&パターンから削除するということは、Option<&str>(文字列スライスへのオプションの参照ではなく、オプションの文字列スライス)と照合しようとしていることを意味します。参照については説明していませんので、現時点では完全には意味がない可能性があります。今のところ&、タイプが正しく並んでいることを確認していることを覚えておいてください。

この例をRustプレイグラウンドで実行してみることができます。

出力は次のとおりです。

It's a delicious banana!
Coconuts are awesome!!!
There is no fruit! :(

文字列値が"coconut"、の場合、最初のアームが照合され、実行のフローを決定するために使用されることに注意してください。

一致式を使用するときは常に、次のルールに注意してください。

  • match腕は上から下に評価されます。特定のケースは、一般的なケースよりも早く定義する必要があります。そうしないと、一致して評価されることはありません。
  • matchアームは、入力タイプが持つ可能性のあるすべての可能な値をカバーする必要があります。網羅的でないパターンリストと照合しようとすると、コンパイラエラーが発生します。

場合のlet表現

Rustは、値が単一のパターンに準拠しているかどうかをテストするための便利な方法を提供します。

次の例では、への入力matchOption<u8>値です。matchその入力値がある場合式はコードのみを実行する必要があります7

let a_number: Option<u8> = Some(7);
match a_number {
    Some(7) => println!("That's my lucky number!"),
    _ => {},
}

この場合、NoneバリアントとSome<u8>一致しないすべての値を無視しますSome(7)。ワイルドカードパターンは、このタイプの状況に役立ちます。あなたは追加することができ_、他の全てのパターンが一致した後(アンダースコア)のワイルドカードパターンを何かをし、マッチ腕を排出するためのコンパイラの需要を満たすために使われています。

このコードを要約するには、iflet式を使用できます。

let a_number: Option<u8> = Some(7);
if let Some(7) = a_number {
    println!("That's my lucky number!");
}

場合のlet演算子は式のパターンを比較します。式がパターンに一致する場合、ifブロックが実行されます。if let式の良いところは、一致する単一のパターンに関心がある場合、一致式の定型コードをすべて必要としないことです。

使用unwrapしてexpect

メソッドOptionを使用して、型の内部値に直接アクセスすることを試みることができますunwrap。ただし、バリアントがNone。の場合、このメソッドはパニックになるので注意してください。

例えば:

let gift = Some("candy");
assert_eq!(gift.unwrap(), "candy");

let empty_gift: Option<&str> = None;
assert_eq!(empty_gift.unwrap(), "candy"); // This will panic!

この場合、コードは次の出力でパニックになります。

    thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:6:27

このexpectメソッドはと同じようunwrapに機能しますが、2番目の引数によって提供されるカスタムパニックメッセージを提供します。

let a = Some("value");
assert_eq!(a.expect("fruits are healthy"), "value");

let b: Option<&str> = None;
b.expect("fruits are healthy"); // panics with `fruits are healthy`

出力は次のとおりです。

    thread 'main' panicked at 'fruits are healthy', src/main.rs:6:7

これらの関数はパニックになる可能性があるため、使用することはお勧めしません。代わりに、次のいずれかのアプローチを検討してください。

  • パターンマッチングを使用して、Noneケースを明示的に処理します。
  • のような同様のパニックにならないメソッドを呼び出しunwrap_orます。これは、バリアントがの場合はデフォルト値を返し、バリアントがのNone場合は内部値を返しますSome(value)
assert_eq!(Some("dog").unwrap_or("cat"), "dog");
assert_eq!(None.unwrap_or("cat"), "cat");

演習-不在に対処するためにオプションタイプを使用する


この演習では、Person構造体を受け取り、Stringそのフルネームを含むを返す関数の実装を終了します。

ミドルネームを持っていない人もいますが、持っている場合は、戻り値に含める必要があることに注意してください。

build_full_name関数のみを編集する必要があります。名と姓を処理する部分はすでに実装されていることに注意してください。

struct Person {
    first: String,
    middle: Option<String>,
    last: String,
}

fn build_full_name(person: &Person) -> String {
    let mut full_name = String::new();
    full_name.push_str(&person.first);
    full_name.push_str(" ");

    // TODO: Implement the part of this function that handles the person's middle name.

    full_name.push_str(&person.last);
    full_name
}

fn main() {
    let john = Person {
        first: String::from("James"),
        middle: Some(String::from("Oliver")),
        last: String::from("Smith"),
    };
    assert_eq!(build_full_name(&john), "James Oliver Smith");

    let alice = Person {
        first: String::from("Alice"),
        middle: None,
        last: String::from("Stevens"),
    };
    assert_eq!(build_full_name(&alice), "Alice Stevens");

    let bob = Person {
        first: String::from("Robert"),
        middle: Some(String::from("Murdock")),
        last: String::from("Jones"),
    };
    assert_eq!(build_full_name(&bob), "Robert Murdock Jones");
}

上記のコードを実行し、すべてのassert_eq!式がパニックにならずに合格することを確認します。Rustプレイグラウンドでコードを編集することもできます。

結果タイプを使用してエラーを処理します

Rustは、Result<T, E>エラーを返したり伝播したりするための列挙型を提供します。慣例により、Ok(T)バリアントは成功を表し、値を含み、バリアントErr(E)はエラーを表し、エラー値を含みます。

Result<T, E>列挙型は次のように定義されています。

enum Result<T, E> {
    Ok(T):  // A value T was obtained.
    Err(E): // An error of type E was encountered instead.
}

Optionない可能性を説明するタイプとは対照的に、タイプResult障害が発生する可能性がある場合に最適です。

このResultタイプには、次のいずれかを実行するunwrapandexpectメソッドもあります。

  • Okこの場合、バリアント内の値を返します。
  • バリアントがErr。の場合、プログラムをパニックにします。

Result実際の動作を見てみましょう。次のサンプルコードには、次のsafe_divisionいずれかを返す関数の実装があります。

  • 成功した除算の結果を運ぶバリアントを持つResultOk
  • Err構造体担持バリアントDivisionByZeroError失敗分割信号を送ります。
#[derive(Debug)]
struct DivisionByZeroError;

fn safe_division(dividend: f64, divisor: f64) -> Result<f64, DivisionByZeroError> {
    if divisor == 0.0 {
        Err(DivisionByZeroError)
    } else {
        Ok(dividend / divisor)
    }
}

fn main() {
    println!("{:?}", safe_division(9.0, 3.0));
    println!("{:?}", safe_division(4.0, 0.0));
    println!("{:?}", safe_division(0.0, 2.0));
}

このプログラムを確認するには、Rustプレイグラウンドにアクセスしてください。

出力は次のとおりです。

Ok(3.0)
Err(DivisionByZeroError)
Ok(0.0)

構造体の#[derive(Debug)]前の部分DivisionByZeroErrorは、デバッグ目的で型を印刷可能にするようにRustコンパイラーに指示するマクロです。この概念については、後の特性モジュールで詳しく説明します。
演習-結果タイプを使用してエラーを処理します

この演習では、コンピューターからファイルを読み取るプログラムにエラー処理を追加します。サンプルプログラムでは、read_file_contents関数はPathBuf単一の入力として構造体を受け取り、を返しますResult<String, io::Error>。この関数は次のタスクを実行します。

  1. 変更可能な空のString変数を作成します。
  2. 指定されたパスにあるファイルにアクセスします。
  3. メソッドを使用して、ファイルの内容をString変数に読み込みますread_to_string
  4. 変更されたString変数を返します。

コードの詳細は次のとおりです。

  • プログラムはいくつかの構造体と特性を使用します。コードの最初の3行は、これらのデータ型をスコープに入れます。
  • 一部のmatchアームは、file_handleやのような変数を導入しますio_error。それらのスコープはmatch式に制限されているため、前のコードでは宣言されていませんmatch
  • このopenメソッドはResult<File, Error>列挙型を返します。エラーが発生しない場合は、Okバリアントでラップされたファイルハンドルを返します。
  • このread_to_stringメソッドは、ファイルの内容をstring戻り値ではなく、渡されたパラメーターに追加します。
  • プログラムは、match式を使用して変数に値を割り当てます。その代入ステートメント内で、matcharmsを使用して関数から早期に戻ることもできます。

サンプルプログラムを開く

この演習のサンプルコードで作業するには、次の2つのオプションがあります。

  • 次のコードをコピーして、ローカル開発環境で編集します。
  • この準備されたRustPlaygroundでコードを開きます。

ローカル開発環境で編集する

ローカルコンピューターでこのコードを実行するには、src /main.rsファイルにコードを記述します。ファイルはCargoプロジェクトのルートにある必要があります。新しいCargoプロジェクトを設定する方法に関する質問については、この学習パスの最初のモジュールを確認してください。

さび遊び場で働く

Rust Playgroundで演習を完了するには、独自のコードをsrc /main.rsという名前のファイルとして読み取ることができます。このファイルは独自の仮想パス上にあります。

use std::fs::File;
use std::io::{Error, Read};
use std::path::PathBuf;

fn read_file_contents(path: PathBuf) -> Result<String, Error> {
    let mut string = String::new();

    // Access a file at a specified path
    // ---------------------------------
    // TODO #1:
    // - Pass variable to `file` variable on success, or
    // - Return from function early if there's an error
    let mut file: File = match File::open(path) {
        Ok(file_handle) => todo!("Pass variable to `file` variable on success"),
        Err(io_error) => todo!("Return from function early if there's an error")
    };

    // Read file contents into `String` variable with `read_to_string`
    // ---------------------------------
    // Success path is already filled in
    // TODO #2: Return from function early if there's an error
    match file.read_to_string(&mut string) {
        Ok(_) => (),
        Err(io_error) => todo!("Return from function early if there's an error")
    };

    // TODO #3: Return `string` variable as expected by function signature
    todo!("Return `string` variable")
}

fn main() {
    if read_file_contents(PathBuf::from("src/main.rs")).is_ok() {
        println!("The program found the main file.");
    }
    if read_file_contents(PathBuf::from("non-existent-file.txt")).is_err() {
        println!("The program reported an error for the file that doesn't exist.");
    }
}

成功と失敗のシナリオを処理する

最初のタスクは、成功と失敗のシナリオを処理するためのコードを追加することです。

ノート

サンプルコードで、TODOコメントとtodo!マクロを探します。コメントは、完了するタスクを説明しています。このマクロは、終了または更新する必要のあるコードを示します。

次のコードを更新して、match式内の成功と失敗のシナリオを処理します。

    // Access a file at a specified path
    // ---------------------------------
    // TODO #1:
    // - Pass variable to `file` variable on success, or
    // - Return from function early if there's an error
    let mut file: File = match File::open(path) {
        Ok(file_handle) => todo!("Pass variable to `file` variable on success"),
        Err(io_error) => todo!("Return from function early if there's an error")
    };

タスクを完了したら、次のプログラミング目標に取り組みます。

  • Ok(value)ケースは、内側を提供する必要がありますvalue
  • Err(error_value)値は早期から返却されなければならないread_file_contents機能。

エラーシナリオを処理する

次のタスクは、エラー処理を追加することです。Errケースをサポートするために、次のコードを更新してください。

    // Read file contents into `String` variable with `read_to_string`
    // ---------------------------------
    // Success path is already filled in
    // TODO #2: Return from function early if there's an error
    match file.read_to_string(&mut string) {
        Ok(_) => (),
        Err(io_error) => todo!("Return from function early if there's an error")
    };

このタスクに取り組むときは、次のプログラミング目標に取り組んでください。

  • Ok(value)ケースは、内側を提供する必要がありますvalue
  • Err(error_value)値は早期から返却されなければならないread_file_contents機能。
     

文字列を返す

最後のタスクは、コードを修正しStringて、Okバリアント内の変更された変数を返すことです。実装は、戻り値が関数の期待される成功した出力であることを表現する必要があります。

fn read_file_contents(path: PathBuf) -> Result<String, Error> {
    ...
    // TODO #3: Return `string` variable as expected by function signature
    todo!("Return `string` variable")
}

プログラムをビルドする

タスクを完了したら、プログラムをビルドして実行します。次の出力が表示されます。

The program found the main file.
The program reported an error for the file that doesn't exist.

概要

このモジュールでは、次のことを学びました。

  • を使用して、回復不能なエラーが発生した場合にRustプログラムを停止する方法panic!
  • 列挙型を使用して値が存在しない可能性を表す方法Option
  • Result列挙型を使用して操作を表す方法。
  • パターンマッチングを使用して、特定のタイプのすべての可能な値に安全にアクセスする方法。

この学習パスの次のモジュールでは、Rustの最も斬新な側面の1つであるボローチェッカーと、コード内のさまざまな種類のバグを防ぐ方法について学習します。

 リンク: https://docs.microsoft.com/en-us/learn/modules/rust-error-handling/

#rust #Beginners