Zenn
🐝

Rust 小ネタ: Debug トレイトを利用した等価比較

2025/03/28に公開

通常、値を (非)等価比較 したいときは PartialEq トレイトを付ける必要がありますが、本記事では Debug トレイトを用いた等価比較の方法と、その注意点について紹介します。

#[derive(Debug)]
struct S;

let foo = S;
let bar = S;
assert!(format!("{foo:?}") == format!("{bar:?}"));

ユーザー定義型の等価比較

ユーザー定義型とは、 i32char などの言語組み込みのプリミティブ型とは異なり、 structenum といった ユーザーが定義する型 のことです。
これらの型は、比較処理や文字列表現の方法がデフォルトで定まっていないため、 PartialEq トレイトや Debug トレイトを自身で実装する必要があります。

比較処理が出てくる場面は主に2つです。

  • if式などの条件
  • assert_eq! マクロなどによる等価性の検証

後者は比較によるアサーションが失敗した時に、値をデバッグ形式で出力するので、Debug トレイトも実装しなければいけません。

Debug トレイトに関しては、デバッグでとても有用であるため、構造体を定義するのと同時に実装する方も多いのではないでしょうか。
一方、 PartialEq トレイトは比較処理が必要になるまで実装しません(自分は)。
実装していない状態で比較を試みると、コンパイルエラーになります。

#[derive(Debug)]
struct Foo {
    bar: i32,
}

let foo = Foo { bar: 1 };
assert!(foo == Foo { bar: 1 });
error[E0369]: binary operation `==` cannot be applied to type `Foo`
   |
   |     assert!(foo == Foo { bar: 1 });
   |             --- ^^ -------------- Foo
   |             |
   |             Foo
   |
note: an implementation of `PartialEq` might be missing for `Foo`
   |
   | struct Foo {
   | ^^^^^^^^^^ must implement `PartialEq`
help: consider annotating `Foo` with `#[derive(PartialEq)]`
   |
   + #[derive(PartialEq)]
   | struct Foo {
   |

Debug のみでの等価比較: format! マクロを使う

この時、 PartialEq を実装するのではなく、PartialEq を実装した別の型へ変換することで、コンパイルエラーを回避できます。
具体的には、format! マクロを使って値を String 型へ変換し、比較を行います。

  #[derive(Debug)]
  struct Foo {
      bar: i32,
  }

  let foo = Foo { bar: 1 };
- assert!(foo == Foo { bar: 1 });
+ assert!(format!("{foo:?}") == format!("{:?}", Foo { bar: 1 }));

必要時のみ PartialEq を実装する: cfg_attr 属性の利用

「プロダクションコードでは比較処理は不要だが、テストコードでは assert_eq! で検証したい」という場合、 cfg_attr 属性が役立ちます。
これにより、テスト時のみ PartialEq トレイトを実装することができ、リリースバイナリのサイズを抑えられます。

#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
struct Foo {
    bar: i32,
}

#[test]
fn test() {
    let foo = Foo { bar: 1 };
    assert!(foo == Foo { bar: 1 });
}

まとめ

Debug トレイトを利用した等価比較のテクニックを紹介しました。
ただし、以下の点に注意する必要があります。

  • 比較に必要な情報が欠落している可能性がある
  • 別の型への変換コストが発生する

したがって、比較処理が必要な場合は、PartialEq トレイトを適切に実装するべきです。

cfg_attrcfg といった 条件付きコンパイル は、他にも様々な条件を指定できます。それらを活用してよりスマートなコードを書きましょう!

……とはいえ、デバッグで手軽に検証したい時なんかは Debug トレイトを利用した方法が楽で良いんじゃないでしょうか。

ここまで書いてなんですが、リリースビルドでは未使用の実装が最適化によって削除される可能性があるため、何も考えずに PartialEq くらいは実装しても良いとも思いました。

フェアリーデバイセズ公式

Discussion

ログインするとコメントできます