Progrust Library.

// dictionary

Debugトレイト.

Debugは、値の中身を開発者向けに表示するための標準ライブラリのトレイトです(std::fmt::Debug)。コンソール出力の{:?}はこのトレイトの実装を呼び出す指定で、{:#?}と書くと整形された複数行で表示されます1。

要求されるメソッドはfmtの1つだけですが、実装はderive属性(#[derive(Debug)])に任せるのが基本です2。デバッグを助けることが目的のトレイトのため、std公式ドキュメントはすべての公開型に実装することを推奨しています1。

#[derive(Debug)]
struct Book {
    title: String,
    price: u32,
}

#[derive(Debug)]
enum Payment {
    Cash,
    Credit(String),
}

fn main() {
    let book = Book {
        title: String::from("Rust入門"),
        price: 3200,
    };
    println!("{:?}", book);  // Book { title: "Rust入門", price: 3200 }
    println!("{:#?}", book); // 1フィールド1行に整形して表示される
    println!("{:?}", Payment::Cash);                          // Cash
    println!("{:?}", Payment::Credit(String::from("JCB")));   // Credit("JCB")
}
Playgroundで開く

導出される出力の形式

#[derive(Debug)]が生成する表示は、型の名前とフィールドの値を機械的に並べたものです3。

対象 出力の形 例
構造体 型名 + { フィールド名: 値, ... } Book { title: "Rust入門", price: 3200 }
データを持たない列挙型のバリアント バリアント名だけ Cash
名前付きフィールドを持たないバリアント・タプル構造体 名前 + (値, ...) Credit("JCB")
名前付きフィールドを持つバリアント 名前 + { フィールド名: 値, ... } Credit { brand: "JCB" }

各フィールドの値もそれぞれのDebug実装で表示されます。文字列型や文字列スライスの値はクオートで囲まれ、改行や"は\n・\"とエスケープされた形で出ます1。

補足

Debugが要求される場面

Debugは表示のためだけでなく、標準ライブラリの各所でトレイト境界として要求されます。

場面 要求される理由
Result型のunwrap・expect 失敗時にErrの中身をパニックのメッセージに出すため(E: Debug)5
assert_eq!などのテスト用マクロ 値が一致しなかったときに両辺を表示するため6
dbg! 式の値を、ソース位置とともに標準エラー出力へ表示するため7

「{:?}を書いていないのにDebugを実装しろと言われる」ときは、たいていこれらが原因です。なおdbg!は渡した式の所有権を奪うため、値を使い続けたい場合はdbg!(&value)と借用を渡します7。

表示内容を自分で決める(debug_struct)

形式を制御したい場合はfmtを手で実装します。Formatterのdebug_structビルダーを使うと、フィールドを並べるだけでderiveと同じ体裁になります8。{:#?}を指定したときの整形にもそのまま対応します(Rust 1.93で確認)。

use std::fmt;

struct Book {
    title: String,
    price: u32,
}

impl fmt::Debug for Book {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Book")
            .field("title", &self.title)
            .field("price", &format_args!("{}円", self.price))
            .finish()
    }
}

fn main() {
    let book = Book {
        title: String::from("Rust入門"),
        price: 3200,
    };
    println!("{:?}", book); // Book { title: "Rust入門", price: 3200円 }
}
Playgroundで開く

format_args!で包んだ値はクオートなしでそのまま埋め込まれます。

出力の形式に依存してはいけない

deriveが生成するDebugの出力も、標準ライブラリが提供する型のDebug実装も安定した仕様ではなく、将来のRustで変わる可能性があります9。出力を文字列として解析したり、テストで完全一致を期待したりするコードは避け、あくまで人が読むためのものとして扱います。

16進数で表示する

{:x?}・{:X?}と書くと、Debug表示に含まれる整数型の値を小文字・大文字の16進数で出力できます10。バイト列を確認するときに便利です。

fn main() {
    let bytes = [255u8, 16, 1];
    println!("{:?}", bytes);  // [255, 16, 1]
    println!("{:x?}", bytes); // [ff, 10, 1]
    println!("{:X?}", bytes); // [FF, 10, 1]
}
Playgroundで開く

Footnotes

  1. std公式ドキュメント — Module std::fmt “fmt::Debug implementations should be implemented for all public types. … The purpose of the Debug trait is to facilitate debugging Rust code. In most cases, using #[derive(Debug)] is sufficient and recommended.” / alternate flag: “#? - pretty-print the Debug formatting (adds linebreaks and indentation)” / エスケープの例: “assert_eq!(format!("{} {:?}", "foo\n", "bar\n"), "foo\n \"bar\\n\"");” ↩ ↩2 ↩3

  2. std公式ドキュメント — Trait Debug トレイト定義はfn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>の1メソッドのみ。“Generally speaking, you should just derive a Debug implementation.” ↩

  3. std公式ドキュメント — Trait Debug “This trait can be used with #[derive] if all fields implement Debug. When derived for structs, it will use the name of the struct, then {, then a comma-separated list of each field’s name and Debug value, then }. For enums, it will use the name of the variant and, if applicable, (, then the Debug values of the fields, then ).” なお、タプル構造体と、名前付きフィールドを持つバリアントの形式は同ドキュメントに明記がなく、実際の出力(Rust 1.93で確認)に基づく。 ↩

  4. std公式ドキュメント — Trait Display “Display is similar to Debug, but Display is for user-facing output, and so cannot be derived.” ↩

  5. std公式ドキュメント — Result::unwrap unwrap・expectはimpl<T, E: Debug> Result<T, E>に定義されている。“Panics if the value is an Err, with a panic message provided by the Err’s value.” ↩

  6. std公式ドキュメント — Macro assert_eq! “On panic, this macro will print the values of the expressions with their debug representations.” ↩

  7. std公式ドキュメント — Macro dbg! “Prints and returns the value of a given expression for quick and dirty debugging.” “The macro works by using the Debug implementation of the type of the given expression to print the value to stderr along with the source location of the macro invocation as well as the source code of the expression.” “Invoking the macro on an expression moves and takes ownership of it before returning the evaluated expression unchanged. If the type of the expression does not implement Copy and you don’t want to give up ownership, you can instead borrow with dbg!(&expr) for some expression expr.” ↩ ↩2

  8. std公式ドキュメント — Formatter::debug_struct “Creates a DebugStruct builder designed to assist with creation of fmt::Debug implementations for structs.” ↩

  9. std公式ドキュメント — Trait Debug “Derived Debug formats are not stable, and so may change with future Rust versions. Additionally, Debug implementations of types provided by the standard library (std, core, alloc, etc.) are not stable, and may also change with future Rust versions.” ↩

  10. std公式ドキュメント — Module std::fmt “x? ⇒ Debug with lower-case hexadecimal integers” / “X? ⇒ Debug with upper-case hexadecimal integers” ↩

この辞書が使われているページ

backlinks 3

  1. 辞書derive属性
  2. 辞書Displayトレイト
  3. 本動かして学ぶRustプログラミング問題集 › 第19章 derive属性と標準トレイト