Menu
Coddy logo textTech

Option Enum の再訪

CoddyのRustジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 17/61。

RustではすでにOption<T>に出会っています。これは、存在する場合と存在しない場合がある値を表すための標準的な列挙型です。ここではOOPの視点から見てみましょう。Optionは単なる列挙型ではなく、null許容値を安全かつ洗練された方法で扱える、強力な組み込みメソッドを備えた型です。

毎回マッチングする代わりに、Option値に対して直接メソッドを使用できます。unwrap_orメソッドは、内部の値が存在する場合はそれを返し、存在しない場合は指定したデフォルト値を返します。

let maybe_name: Option<String> = Some(String::from("Alice"));
let name = maybe_name.unwrap_or(String::from("Guest"));
println!("{}", name);  // Alice

let empty: Option<String> = None;
let fallback = empty.unwrap_or(String::from("Guest"));
println!("{}", fallback);  // Guest

mapメソッドは、内部の値が存在する場合にそれを変換し、Noneは変更しません。これにより、常にNoneを確認せずに操作をチェーンできます。

let maybe_num: Option<i32> = Some(5);
let doubled = maybe_num.map(|x| x * 2);
println!("{:?}", doubled);  // Some(10)

let nothing: Option<i32> = None;
let still_nothing = nothing.map(|x| x * 2);
println!("{:?}", still_nothing);  // None

これらのメソッドはOOPの原則を体現しています。Option型は「値があるかもしれない」という概念をカプセル化し、それを扱うためのすっきりしたインターフェースを提供します。外部のロジックを書いてオブジェクトを検査するのではなく、オブジェクトに対してメソッドを呼び出します。

challenge icon

チャレンジ

簡単

Optionメソッドを使用して、オプショナルなデータを適切に処理するユーザープロフィールシステムを構築しましょう。ユーザーには nickname が設定されている場合も、設定されていない場合もあります。明示的なマッチングを行わずに、システムで両方のケースを洗練された方法で処理します。

コードを整理するために、2つのファイルを作成します。

  • profile.rs:2つのフィールド、username(String)とnickname(Option<String>)を持つ public なProfile構造体をDefineします。以下をImplementします。
    • username と optional な nickname を受け取るnewコンストラクター
    • nickname がexistsする場合はそれを返し、そうでなければ username を fallback として返すdisplay_nameメソッド。この処理にはunwrap_orを使用します
    • nickname がexistsする場合は、nickname を brackets で囲んだOption<String>"[CoolNick]"のような形式)を返し、nickname がない場合はNoneを返すformatted_nicknameメソッド。この値の変換にはmapを使用します
  • main.rs:profile モジュールを取り込み、nickname がある Profile とない Profile の2つを作成します。それぞれの Profile で両方のメソッドを実行し、Optionメソッドが値の存在時と不在時をどのように異なる方法で処理するかを示します。

formatted_nicknameメソッドでは、Optionを返すため、出力時にデバッグフォーマット({:?})を使用します。

出力は、両方の Profile を示す次の正確な形式に従う必要があります。

Display name: {name}
Formatted nickname: {option}
Display name: {name}
Formatted nickname: {option}

たとえば、username "alice"、nickname "Ace"の Profile と、nickname がない username "bob"の Profile の場合、出力は次のようになります。

Display name: Ace
Formatted nickname: Some("[Ace]")
Display name: bob
Formatted nickname: None

3つの入力を受け取ります。1つ目の username、1人目のユーザーの nickname、そして nickname を持たない2つ目の username です。

自分で試してみよう

mod profile;

use profile::Profile;

fn main() {
    // 入力を読み取る
    let mut input1 = String::new();
    std::io::stdin().read_line(&mut input1).expect("Failed to read line");
    let username1 = input1.trim().to_string();

    let mut input2 = String::new();
    std::io::stdin().read_line(&mut input2).expect("Failed to read line");
    let nickname1 = input2.trim().to_string();

    let mut input3 = String::new();
    std::io::stdin().read_line(&mut input3).expect("Failed to read line");
    let username2 = input3.trim().to_string();

    // TODO: username1 と nickname1 で最初のプロフィールを作成する (Some)
    
    // TODO: Create second profile with username2 and no nickname (None)
    
    // TODO: Print display_name and formatted_nickname for first profile
    // Use {:?} for formatted_nickname since it returns an Option
    
    // TODO: Print display_name and formatted_nickname for second profile
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Rustオンラインコンパイラ