演習: ハッシュ マップを使用して注文を追跡する

完了

この演習では、ハッシュ マップを使用するために自動車工場プログラムを変更します。

ハッシュ マップ キーと値のペアを使用して、自動車の注文に関する詳細を追跡し、出力を表示します。 ここでも、課題は、コンパイルして実行できるようにサンプル コードを完成させることです。

この演習用のサンプル コードを操作するには、次の 2 つの方法があります。

  • コードをコピーし、ローカルの開発環境で編集します。
  • 準備済みの Rust Playground 内でコードを開きます。

注意

サンプル コードで、todo! マクロを探します。 このマクロは、完了または更新する必要があるコードを示しています。

現在のプログラムを読み込む

最初の手順では、既存のプログラム コードを取得します。

  1. 編集する既存のプログラム コードを開きます。 このコードには、データ型の宣言と、car_qualitycar_factory、および main 関数の定義が含まれています。

    次のコードをコピーし、ローカルの開発環境で編集します。
    または、この準備済みの Rust Playground 内でコードを開きます。

    #[derive(PartialEq, Debug)]
    struct Car { color: String, motor: Transmission, roof: bool, age: (Age, u32) }
    
    #[derive(PartialEq, Debug)]
    enum Transmission { Manual, SemiAuto, Automatic }
    
    #[derive(PartialEq, Debug)]
    enum Age { New, Used }
    
    // Get the car quality by testing the value of the input argument
    // - miles (u32)
    // Return tuple with car age ("New" or "Used") and mileage
    fn car_quality (miles: u32) -> (Age, u32) {
    
        // Check if car has accumulated miles
        // Return tuple early for Used car
        if miles > 0 {
            return (Age::Used, miles);
        }
    
        // Return tuple for New car, no need for "return" keyword or semicolon
        (Age::New, miles)
    }
    
    // Build "Car" using input arguments
    fn car_factory(order: i32, miles: u32) -> Car {
        let colors = ["Blue", "Green", "Red", "Silver"];
    
        // Prevent panic: Check color index for colors array, reset as needed
        // Valid color = 1, 2, 3, or 4
        // If color > 4, reduce color to valid index
        let mut color = order as usize;
        if color > 4 {        
            // color = 5 --> index 1, 6 --> 2, 7 --> 3, 8 --> 4
            color = color - 4;
        }
    
        // Add variety to orders for motor type and roof type
        let mut motor = Transmission::Manual;
        let mut roof = true;
        if order % 3 == 0 {          // 3, 6, 9
            motor = Transmission::Automatic;
        } else if order % 2 == 0 {   // 2, 4, 8, 10
            motor = Transmission::SemiAuto;
            roof = false;
        }                            // 1, 5, 7, 11
    
        // Return requested "Car"
        Car {
            color: String::from(colors[(color-1) as usize]),
            motor: motor,
            roof: roof,
            age: car_quality(miles)
        }
    }
    
    fn main() {
        // Initialize counter variable
        let mut order = 1;
        // Declare a car as mutable "Car" struct
        let mut car: Car;
    
        // Order 6 cars, increment "order" for each request
        // Car order #1: Used, Hard top
        car = car_factory(order, 1000);
        println!("{}: {:?}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);
    
        // Car order #2: Used, Convertible
        order = order + 1;
        car = car_factory(order, 2000);
        println!("{}: {:?}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);    
    
        // Car order #3: New, Hard top
        order = order + 1;
        car = car_factory(order, 0);
        println!("{}: {:?}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);
    
        // Car order #4: New, Convertible
        order = order + 1;
        car = car_factory(order, 0);
        println!("{}: {:?}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);
    
        // Car order #5: Used, Hard top
        order = order + 1;
        car = car_factory(order, 3000);
        println!("{}: {:?}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);
    
        // Car order #6: Used, Hard top
        order = order + 1;
        car = car_factory(order, 4000);
        println!("{}: {:?}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);
    }
    
  2. プログラムをビルドします。 次のセクションに進む前に、コードがコンパイルされ、実行されることを確認してください。

次の出力が表示されます。

1: Used, Hard top = true, Manual, Blue, 1000 miles
2: Used, Hard top = false, SemiAuto, Green, 2000 miles
3: New, Hard top = true, Automatic, Red, 0 miles
4: New, Hard top = false, SemiAuto, Silver, 0 miles
5: Used, Hard top = true, Manual, Blue, 3000 miles
6: Used, Hard top = true, Automatic, Green, 4000 miles

注文の詳細を追跡するハッシュ マップを追加する

現在のプログラムでは、各自動車の注文が満たされ、各注文の完了後に概要が出力されます。 car_factory 関数を呼び出すたびに、注文の詳細を含む Car 構造体が返されて注文が満たされます。 結果は car 変数に格納されます。

お気付きかもしれませんが、プログラムにはいくつかの重要な機能がありません。 すべての注文を追跡しているわけではありません。 car 変数には、現在の注文の詳細のみが保持されます。 car_factory 関数からの結果で car 変数が更新されるたびに、前の注文の詳細が上書きされます。

ファイリング システムの場合のようにすべての注文を追跡するには、プログラムを更新する必要があります。 このために、<K、V> ペアでハッシュ マップを定義します。 ハッシュ マップ キーは自動車の注文番号に対応します。 ハッシュ マップ値は、Car 構造体で定義されているそれぞれの注文の詳細となります。

  1. ハッシュ マップを定義するには、main 関数の先頭の左中かっこ { の直後に次のコードを追加します。

        // Initialize a hash map for the car orders
        // - Key: Car order number, i32
        // - Value: Car order details, Car struct
        use std::collections::HashMap;
        let mut orders: HashMap<i32, Car> = HashMap;
    
  2. orders ハッシュ マップを作成するステートメントの構文の問題を修正します。

    ヒント

    ハッシュ マップを最初から作成しているので、おそらく new() メソッドを使用します。

  3. プログラムをビルドします。 次のセクションに進む前に、コードがコンパイルされることを確認してください。 コンパイラからの警告メッセージは無視してかまいません。

ハッシュ マップに値を追加する

次の手順では、ハッシュ マップに自動車の満たされた各注文を追加します。

main 関数では、自動車の各注文に対して car_factory 関数を呼び出します。 注文が満たされた後、println! マクロを呼び出して、car 変数に格納されている注文の詳細を表示します。

    // Car order #1: Used, Hard top
    car = car_factory(order, 1000);
    println!("{}: {}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);

    ...

    // Car order #6: Used, Hard top
    order = order + 1;
    car = car_factory(order, 4000);
    println!("{}: {}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);

新しいハッシュ マップを操作するために、これらのコード ステートメントを修正します。

  • car_factory 関数の呼び出しを保持する。 返された各 Car 構造体は、ハッシュ マップに <K、V> ペアの一部として格納されます。
  • println! マクロの呼び出しを更新して、ハッシュ マップに格納されている注文の詳細を表示する。
  1. main 関数で、car_factory 関数の呼び出しと、println! マクロの付随する呼び出しを見つけます。

        // Car order #1: Used, Hard top
        car = car_factory(order, 1000);
        println!("{}: {}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);
    
        ...
    
        // Car order #6: Used, Hard top
        order = order + 1;
        car = car_factory(order, 4000);
        println!("{}: {}, Hard top = {}, {:?}, {}, {} miles", order, car.age.0, car.roof, car.motor, car.color, car.age.1);
    
  2. すべての自動車注文のステートメントの完全なセットを、次の修正後のコードに置き換えます。

        // Car order #1: Used, Hard top
        car = car_factory(order, 1000);
        orders(order, car);
        println!("Car order {}: {:?}", order, orders.get(&order));
    
        // Car order #2: Used, Convertible
        order = order + 1;
        car = car_factory(order, 2000);
        orders(order, car);
        println!("Car order {}: {:?}", order, orders.get(&order));
    
        // Car order #3: New, Hard top
        order = order + 1;
        car = car_factory(order, 0);
        orders(order, car);
        println!("Car order {}: {:?}", order, orders.get(&order));
    
        // Car order #4: New, Convertible
        order = order + 1;
        car = car_factory(order, 0);
        orders(order, car);
        println!("Car order {}: {:?}", order, orders.get(&order));
    
        // Car order #5: Used, Hard top
        order = order + 1;
        car = car_factory(order, 3000);
        orders(order, car);
        println!("Car order {}: {:?}", order, orders.get(&order));
    
        // Car order #6: Used, Hard top
        order = order + 1;
        car = car_factory(order, 4000);
        orders(order, car);
        println!("Car order {}: {:?}", order, orders.get(&order));
    
  3. ここでプログラムをビルドしようとすると、コンパイル エラーが表示されます。 <K、V> ペアを orders ハッシュ マップに追加するステートメントには、構文の問題があります。 その問題がわかりますか。 それでは、注文をハッシュ マップに追加する各ステートメントの問題を修正しましょう。

    ヒント

    orders ハッシュ マップに値を直接割り当てることはできません。 挿入を行うには、メソッドを使用する必要があります。

プログラムの実行

プログラムが正常にビルドされた後、次の出力が表示されるはずです。

Car order 1: Some(Car { color: "Blue", motor: Manual, roof: true, age: ("Used", 1000) })
Car order 2: Some(Car { color: "Green", motor: SemiAuto, roof: false, age: ("Used", 2000) })
Car order 3: Some(Car { color: "Red", motor: Automatic, roof: true, age: ("New", 0) })
Car order 4: Some(Car { color: "Silver", motor: SemiAuto, roof: false, age: ("New", 0) })
Car order 5: Some(Car { color: "Blue", motor: Manual, roof: true, age: ("Used", 3000) })
Car order 6: Some(Car { color: "Green", motor: Automatic, roof: true, age: ("Used", 4000) })

変更後のコードの出力が異なることに注目してください。 println! マクロでは、各値と対応するフィールド名を表示することによって、Car 構造体の内容を表示します。

次の演習では、ループ式を使用してコード内の冗長性を減らします。

解決策

この Rust Playground 内で、プログラム出力をこの演習のソリューションと比較できます。