Rustのdbg!マクロについて
dbg!
マクロとは
Rust1.32.0 より stable に追加されたマクロです。
その名の通り、デバッグに便利なマクロです。
式と結果が標準エラーに出力されます。
println!
などのマクロと違ってそのまま式が出力されます。出力後に値を返してくれるのが大きな違いです。
利用方法
dbg!
マクロにて出力する値はDebug
トレイトを実装している必要があります。
#[allow(dead_code)]
struct S(usize);
fn main() {
dbg!("rust"); // &strはDebugトレイトを実装している。
// dbg!(S(0)); // SはDebugトレイトを実装していないためエラー
}
[src/main.rs:5] "rust" = "rust"
式の結果がDebug
トレイトを実装していればよく、式をそのまま渡すこともできます。
fn main() {
dbg!(1 + 2 + 3 + 4);
}
[src/main.rs:2] 1 + 2 + 3 + 4 = 10
式の結果がCopy
トレイトを実装していない場合、値渡しをした場合、元の所有権は失われます。
Copy
トレイトを実装してなくても参照渡しをすれば所有権は保持されます。
#![allow(unused_variables)]
#[derive(Debug)]
struct S(usize);
fn main() {
let a = dbg!(S(1 + 2));
let b = dbg!(a.0 + 1);
// let c = dbg!(a).0 + 2; // Sはコピートレイトを実装していないためコンパイルエラー
let d = dbg!(&a.0 + 1); // 参照渡しはOK
}
[src/main.rs:7] S(1 + 2) = S(
3,
)
[src/main.rs:8] a.0 + 1 = 4
[src/main.rs:10] &a.0 + 1 = 4
Copy
トレイトを実装していれば、元の所有権は残ったままとなります。
#![allow(unused_variables)]
#[derive(Copy, Clone, Debug)]
struct S(usize);
fn main() {
let a = dbg!(S(1 + 2));
let b = dbg!(a.0 + 1);
let c = dbg!(a).0 + 2; //SがCopyトレイトを実装しているためOK
let d = dbg!(&a.0 + 1); // 参照渡しもOK
}
[src/main.rs:7] S(1 + 2) = S(
3,
)
[src/main.rs:8] a.0 + 1 = 4
[src/main.rs:9] a = S(
3,
)
[src/main.rs:10] &a.0 + 1 = 4
実用的な例としては、イテレータの途中経過をデバッグするとき等に利用できます。
#![allow(unused_variables)]
fn main() {
let x = (0..5)
.map(|x| dbg!(x + 9))
.map(|x| dbg!(x * 7))
.fold(0, |acc, x| dbg!(acc + x));
}
[src/main.rs:5] x + 9 = 9
[src/main.rs:6] x * 7 = 63
[src/main.rs:7] acc + x = 63
[src/main.rs:5] x + 9 = 10
[src/main.rs:6] x * 7 = 70
[src/main.rs:7] acc + x = 133
[src/main.rs:5] x + 9 = 11
[src/main.rs:6] x * 7 = 77
[src/main.rs:7] acc + x = 210
[src/main.rs:5] x + 9 = 12
[src/main.rs:6] x * 7 = 84
[src/main.rs:7] acc + x = 294
[src/main.rs:5] x + 9 = 13
[src/main.rs:6] x * 7 = 91
[src/main.rs:7] acc + x = 385
引数に何もわたさないとdbg!
マクロ記述のファイル名と行が出力されます。
fn main() {
dbg!()
}
[src/main.rs:2]
複数の式を渡すこともできます。戻り値はタプルで帰ってきます。
#![feature(type_name_of_val)]
fn main() {
let a = dbg!(1 + 2, 3 + 4, 5 + 6, 7 + 8);
dbg!(std::any::type_name_of_val(&a));
}
[src/main.rs:3] 1 + 2 = 3
[src/main.rs:3] 3 + 4 = 7
[src/main.rs:3] 5 + 6 = 11
[src/main.rs:3] 7 + 8 = 15
[src/main.rs:4] std::any::type_name_of_val(&a) = "(i32, i32, i32, i32)"
dbg!
マクロ利用で警告/エラーを表示 - Clippy との連携
dbg!
マクロは release ビルド時でも出力されます。
また、バージョン管理システムでの dbg!
マクロ利用をエラーにしたいなどがあります。
これらは Rust の lint ツールである Clippy を利用することで対策できます。
clippy をインストール後、cargo check
コマンドのように cargo clippy
コマンドを利用できます。
dbg!
マクロで警告/エラーを表示する場合、clippy::dbg_macro
のレベルを変更することで表示できます。
設定方法には大きく分けて 2 通りあり、Rust の属性で設定する方法と、cargo clippy
のコマンドラインオプションで指定する方法があります。
-
属性での指定
warn
属性で警告が発生、deny
属性ではエラーが発生します。main.rs#![warn(clippy::dbg_macro)] // 警告 // #![deny(clippy::dbg_macro)] // エラー fn main() { dbg!(); }
cargo clippy
の結果(一部抜粋)-
警告時
warning: `dbg!` macro is intended as a debugging tool --> src/main.rs:4:5 | 4 | dbg!(); | ^^^^^^ |
-
エラー時
error: `dbg!` macro is intended as a debugging tool --> src/main.rs:4:5 | 4 | dbg!(); | ^^^^^^ |
-
-
コマンドラインオプションでの指定
Git の pre-commit や CI 等を使いチェックをするならコマンドラインで指定するのが便利です。
-
警告表示
cargo clippy -- -W clippy::dbg_macro
-
エラー表示
cargo clippy -- -D clippy::dbg_macro
Git の pre-commit の一例としては以下のような内容になります。
#!/bin/bash -eu cargo clean && cargo clippy -- -D clippy::dbg_macro
-
rust-analyzer との連携
dbg!
マクロは LSP である rust-analyzer を利用することで便利な機能があります。
rust-analyzer はさまざまなエディタで利用できますが、本記事では VSCode を利用します。
dbg!
マクロの挿入
Magic Completions と呼ばれる機能を使うことでお手軽にdbg!
マクロを挿入できます。
-
expr.dbg
let x = 1; x.dbg💡
💡 位置でカーソルがある状態で
Tab
キーを押すと以下に変換してくれます。let x = 1; dbg!(x)
-
expr.dbgr
let x = 1; x.dbgr💡
💡 位置でカーソルがある状態で
Tab
キーを押すと以下に変換してくれます。let x = 1; dbg!(&x)
dbg!
マクロの削除
dbg!
マクロにカーソルがある状態でCtrl + .
またはエディタの左側に表示される 💡 アイコンをクリックすると、
Remove dbg!()
メニューが表示され、それをクリックするとdbg!
マクロを削除します。
💡dbg!(1)
Remove dbg!()
を実行するとdbg!
マクロを削除してくれます。
1
clippy の利用
rust-analyzer はデフォルトでは保存時にcargo check
を実行をしますが、設定を変更することにより、cargo clippy
を実行できます。
VSCode の settings.json の設定例。
{
// 保存時のコマンドをcargo checkから cargo clippyに変更します。
"rust-analyzer.checkOnSave.command": "clippy",
// 保存時のコマンドの引数を指定。ここではdbg!マクロ利用時に警告が出るようにします。
"rust-analyzer.checkOnSave.extraArgs": ["--", "-W", "clippy::dbg-macro"],
}
Discussion