はじめに
これはあくまでメモなので、基本的にググってわかるようなことは詳しく書きません。
よろしくお願いします。
また、以下のプログラムでは面倒なのでfn main() {}
を省略することがあります。
ご承知おきください。
おさらい
前回は2版の5章の5.1に目を通して、以下のことを学びました。
- 構造体の基礎
以下のリンクで参照できます。
https://qiita.com/task4233/items/3bb3474df7b52b64d864
5. 構造体
構造体を用いたプログラム
以下のlist1
のプログラムを見てみましょう。
このプログラムではwidth
とheight
をもとに長方形の面積を求めるプログラムです。
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
を使用することでそれを解消できます。
なぜなら引数として必要なのは、インスタンス化したオブジェクトのみだからです。
ただし、関数呼び出し時に&
を用いて借用していることに注意してください。
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
のようになります。
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
のようになります。
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
のようになりエラーが消えます。
#[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
のようになります。
#[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
関数をメソッドとして実装してみようと思います。