LoginSignup
3
3

More than 5 years have passed since last update.

Rust Tutorial[2版] めも 5章 5.2.構造体-2

Posted at

はじめに

これはあくまでメモなので、基本的にググってわかるようなことは詳しく書きません。
よろしくお願いします。

また、以下のプログラムでは面倒なのでfn main() {}を省略することがあります。
ご承知おきください。

おさらい

前回は2版の5章の5.1に目を通して、以下のことを学びました。

  • 構造体の基礎

以下のリンクで参照できます。
https://qiita.com/task4233/items/3bb3474df7b52b64d864

5. 構造体

構造体を用いたプログラム

以下のlist1のプログラムを見てみましょう。
このプログラムではwidthheightをもとに長方形の面積を求めるプログラムです。

list1
fn main() {
    let width1 = 30;
    let height1 = 50;

    // 四角形の面積は、{}平方ピクセルです
    println!(
        "The area of the rectangle is {} square pixels.",
        area(width1, height1)
    );
}

fn area(width: u32, height: u32) -> u32 {
    width * height
}

これでも動きますが、長方形の種類を増やした場合はどうでしょうか?
その場合は、width2, width3, width4,...と番号付けしていくのでしょうか?

仮にそうしたとすると、typoでarea関数の呼び出し時にarea(width2, height3)などとしてしまう危険性があります。

それは、以下のlist2のように構造体Rectangleを使用することでそれを解消できます。
なぜなら引数として必要なのは、インスタンス化したオブジェクトのみだからです。
ただし、関数呼び出し時に&を用いて借用していることに注意してください。

list2
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!(
        "The area of the rectangle is {} square pixels.",
        area(&rect1)
    );
}

fn area(rectangle: &Rectangle) -> u32 {
    rectangle.width * rectangle.height
}

構造体に有用な機能を追加する

デバッグ時にlist2の構造体Rectangleのインスタンスのフィールドを出力できたら嬉しいですよね。
しかもprintln!だけで。

とりあえずやってみると、以下のlist3のようになります。

list3
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!("rect is {}", rect1);
}

// [output]
// error[E0277]: the trait bound `Rectangle: std::fmt::Display` is not satisfied
// (エラー: トレイト境界`Rectangle: std::fmt::Display`が満たされてないよ)
//  --> prog.rs:9:28
//   |
// 9 |     println!("rect is {}", rect1);
//   |                            ^^^^^ the trait `std::fmt::Display` is not implemented for `Rectangle`
//                       (`Rectangle`のためにトレイト`std::fmt::Display`が実装されてないよ)
//   |
//   = note: `Rectangle` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
//   (注釈: `Rectangle`はデフォルトのフォーマッタだとフォーマットできないよ。
//         もし君がフォーマット文字列を使ってんなら`:?`を試してみたら?)
//   = note: required by `std::fmt::Display::fmt`
//    (注釈: `std::fmt::Display::fmt`を必要としてるよ)
// 
// error: aborting due to previous error

どうやら、というか当然かもしれませんが、デフォルトのフォーマッタだとフォーマットできないようです。

「フォーマット文字列の代わりに:?を使ってみたら?」と言われたので使ってみると、以下のlist4のようになります。

list4
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!("rect is {:?}", rect1);
}

// [output]
// error[E0277]: the trait bound `Rectangle: std::fmt::Debug` is not satisfied
// (エラー: トレイト境界 `Rectangle: std::fmt::Debug`が満たされてないよ)
// --> prog.rs:9:30
//  |
//9 |     println!("rect is {:?}", rect1);
//  |                              ^^^^^ the trait `std::fmt::Debug` is not implemented for `Rectangle`
//                                       (`Rectangle`のためにトレイト`std::fmt::Debug`は`Rectangle`は実装されてないよ)
//  |
//  = note: `Rectangle` cannot be formatted using `:?`; if it is defined in your crate, add `#[derive(Debug)]` or manually implement it
//    (注釈: `Rectangle`は`:?`を使ってフォーマットできないよ
//          もし君のクレートで定義されてるなら、`#[derive(Debug)]`を追加するか手動で実装してね)
//  = note: required by `std::fmt::Debug::fmt`
//    (注釈: `std::fmt::Display::fmt`を必要としてるよ)
//
//error: aborting due to previous error

どうやら構造体Rectangle#[derive(Debug)]を追加すれば良いようです。

そうすると、以下のlist5のようになりエラーが消えます。

list5
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!("rect is {:?}", rect1);
}

// [output]
// rect is Rectangle { width: 30, height: 50 }

さらに、println!{:?}の代わりに{:#?}を用いると、出力が以下のlist6のようになります。

list6
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!("rect is {:#?}", rect1);
}

// [output]
// rect is Rectangle {
//    width: 30,
//    height: 50
//}

Rustには、derive注釈で使えるトレイトが多く提供されており、独自の型に有用な振る舞いを追加することができます。 そのようなトレイトとその振る舞いは、付録Cで一覧になっています。 これらのトレイトを独自の動作とともに実装する方法だけでなく、独自のトレイトを生成する方法については、第10章で解説します。

だそうです。
今回の例で言えば、トレイトはデバッグ時に便利そうですね。

おわりに

2版5章5.2では以下のことが書かれていました。

  • 構造体を利用したプログラム例
    • 関数呼び出しの時は&をつける
  • 構造体のインスタンスをprintln!でデバッグできるようにする方法
    • 構造体の前に[derive(Debug)]をつける
    • println!("{:?}")とすると1行で出力される
    • println!("{:#?}")とすると改行されて出力される

構造体は複数のフィールドを持つ場合に便利ですね。
次は、初めの方で利用したarea関数をメソッドとして実装してみようと思います。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3