1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

color-eyre × hooq = 💪

Last updated at Posted at 2025-12-08

本記事で伝えたいこと

  • hooq属性マクロはcolor-eyreと併用が可能です!
  • どちらが使いやすいか、どのように使うのがベストなのか、読者に決めてほしいなぁ~

記事内ではコード例を記載しています!

こんにチュア!本記事は hooqアドベントカレンダー 8日目の記事です!

hooqアドベントカレンダーでは筆者が作った属性マクロhooqの話題を提供してきました!

???「hooqマクロとか意味なくない?」

筆者「...だ、誰だ!!!8カ月も掛けて作ったマクロを『無意味だ』なんて言うやつは...!」

???「ウェーイ!最高にイカすエラーレポートハンドラー、color-eyre だぜ!俺っちhooqマクロクンが何したいのかイマイチわかんねーんだよね」

筆者「hooqマクロを関数に付与すれば、エラーが発生した時に 何行目でどの式にてエラーが発生したか をロギングできるんだ!」

停止条件を忘れた再帰関数(hooq利用)
use hooq::hooq;

#[hooq]
fn rec(i: i32) -> Result<(), String> {
    if i <= 0 {
        return Err(format!("i must be positive: i = {i}"));
    }

    rec(i - 1)?;

    Ok(())
}

#[hooq]
fn main() -> Result<(), String> {
    rec(2)?;

    Ok(())
}
実行結果
$ cargo run -q
[src/main.rs:6:9] "i must be positive: i = 0"
   6>    return Err(format!("i must be positive: i = {i}"))
    |
[src/main.rs:9:15] "i must be positive: i = 0"
   9>    rec(i - 1)?
    |
[src/main.rs:9:15] "i must be positive: i = 0"
   9>    rec(i - 1)?
    |
[src/main.rs:16:11] "i must be positive: i = 0"
  16>    rec(2)?
    |
Error: "i must be positive: i = 0"

color-eyre「でもそれ俺っち入れて RUST_LIB_BACKTRACE=full で環境変数設定して実行すれば良くない?」

停止条件を忘れた再帰関数(color-eyre利用)
use color_eyre::Result;
use color_eyre::eyre::bail;

fn rec(i: i32) -> Result<()> {
    if i <= 0 {
        bail!("i must be positive: i = {i}");
    }

    rec(i - 1)?;

    Ok(())
}

fn main() -> Result<()> {
    color_eyre::install()?;

    rec(2)?;

    Ok(())
}
実行結果
$ RUST_LIB_BACKTRACE=full cargo run -q
Error: 
   0: i must be positive: i = 0

Location:
   src/main.rs:6

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                                ⋮ 6 frames hidden ⋮                               
   7: project::rec::he0813d27dc0efcd9
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-1/project/src/main.rs:6
         4 │ fn rec(i: i32) -> Result<()> {
         5 │     if i <= 0 {
         6 >         bail!("i must be positive: i = {i}");
         7 │     }
         8 │ 
   8: project::rec::he0813d27dc0efcd9
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-1/project/src/main.rs:9
         7 │     }
         8 │ 
         9 >     rec(i - 1)?;
        10 │ 
        11 │     Ok(())
   9: project::rec::he0813d27dc0efcd9
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-1/project/src/main.rs:9
         7 │     }
         8 │ 
         9 >     rec(i - 1)?;
        10 │ 
        11 │     Ok(())
  10: project::main::h33fdf4be197c9e8b
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-1/project/src/main.rs:17
        15 │     color_eyre::install()?;
        16 │ 
        17 >     rec(2)?;
        18 │ 
        19 │     Ok(())
  11: core::ops::function::FnOnce::call_once::h6a3f6cda2169809d
      at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250
       248 │     /// Performs the call operation.
       249 │     #[unstable(feature = "fn_traits", issue = "29625")]
       250 >     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
       251 │ }
       252 │ 
  12: std::sys::backtrace::__rust_begin_short_backtrace::h70f5fbbe40f8e036
      at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:158
       156 │     F: FnOnce() -> T,
       157 │ {
       158 >     let result = f();
       159 │ 
       160 │     // prevent this frame from being tail-call optimised away
                                ⋮ 14 frames hidden ⋮                              

Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.

筆者「ぐぬぬ...」

併用例1: 簡単な例

筆者「しかし、 RUST_LIB_BACKTRACE=full を付けなければならないのは忘れがちだし、何よりcolor-eyreは表示がうるさい気がする...」

color-eyre「それは個人の感想じゃね?」

筆者「では読者に決めてもらおう!hooqはcolor-eyreとも併用可能なので、

  • RUST_LIB_BACKTRACE=full があるからcolor-eyreだけでいい
  • 環境変数を設定しなくても見やすいエラートレースが見れるからcolor-eyre & hooqがいい

この二択から決めてほしい!」

color-eyre「しゃーねーな!今回はhooqクンと共に使われてやるぜ!」

プログラム「で、俺が産まれたってわけ :baby: ↓」

Cargo.toml
Cargo.toml
[package]
name = "project"
version = "0.1.0"
edition = "2024"

[dependencies]
color-eyre = "0.6.5"
hooq = "0.3.1"
src/main.rs
use color_eyre::Result;
use color_eyre::eyre::eyre;
use hooq::hooq;

#[hooq(color_eyre)]
fn rec(i: i32) -> Result<()> {
    if i <= 0 {
        // bail だとフックされないのでeyreを利用
        return Err(eyre!("i must be positive: i = {i}"));
    }

    rec(i - 1)?;

    Ok(())
}

#[hooq(color_eyre)]
fn main() -> Result<()> {
    color_eyre::install()?;

    rec(2)?;

    Ok(())
}

color-eyre「実行結果はさらに派手になったぜ!正直過剰...引くわ~」

筆者「🥺」

実行結果
$ RUST_LIB_BACKTRACE=full cargo run -q
Error: 
   0: [src/main.rs:21:11]
        21>    rec(2)?
          |
   1: [src/main.rs:12:15]
        12>    rec(i - 1)?
          |
   2: [src/main.rs:12:15]
        12>    rec(i - 1)?
          |
   3: [src/main.rs:9:9]
         9>    return Err(eyre!("i must be positive: i = {i}"))
          |
   4: i must be positive: i = 0

Location:
   src/main.rs:9

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                                ⋮ 6 frames hidden ⋮                               
   7: project::rec::hd63f2707e105f278
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-1/project/src/main.rs:9
         7 │     if i <= 0 {
         8 │         // bail だとフックされないのでeyreを利用
         9 >         return Err(eyre!("i must be positive: i = {i}"));
        10 │     }
        11 │ 
   8: project::rec::hd63f2707e105f278
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-1/project/src/main.rs:12
        10 │     }
        11 │ 
        12 >     rec(i - 1)?;
        13 │ 
        14 │     Ok(())
   9: project::rec::hd63f2707e105f278
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-1/project/src/main.rs:12
        10 │     }
        11 │ 
        12 >     rec(i - 1)?;
        13 │ 
        14 │     Ok(())
  10: project::main::h40725e9dac4ec709
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-1/project/src/main.rs:21
        19 │     color_eyre::install()?;
        20 │ 
        21 >     rec(2)?;
        22 │ 
        23 │     Ok(())
  11: core::ops::function::FnOnce::call_once::hd2f0f1f0dd1d79b9
      at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250
       248 │     /// Performs the call operation.
       249 │     #[unstable(feature = "fn_traits", issue = "29625")]
       250 >     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
       251 │ }
       252 │ 
  12: std::sys::backtrace::__rust_begin_short_backtrace::h570170a081687a69
      at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:158
       156 │     F: FnOnce() -> T,
       157 │ {
       158 >     let result = f();
       159 │ 
       160 │     // prevent this frame from being tail-call optimised away
                                ⋮ 14 frames hidden ⋮                              

Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.

color-eyre「最初のがhooqクンの出力で、後ろが俺っちな!」

併用例2: color-eyre公式例

color-eyre「俺っちはtracingクレートのSpanTraceとも相性ばっちりなんだけどその辺はどうなんよ?」

筆者「 #[tracing::instrument] とも併用可能です!」

color-eyre「じゃあいい機会だしもっと派手派手なエラー表示にしようぜ!」

筆者「公式の例を引っ張ってきました!」

Cargo.toml
Cargo.toml
[package]
name = "project"
version = "0.1.0"
edition = "2024"

[features]
default = ["capture-spantrace"]
capture-spantrace = []

[dependencies]
color-eyre = "0.6.5"
hooq = "0.3.1"
tracing = "0.1.43"
tracing-error = "0.2.1"
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
src/main.rs
use color_eyre::{Section, eyre::Report, eyre::WrapErr};
use hooq::hooq;
use tracing::{info, instrument};

#[hooq(color_eyre)]
#[instrument]
fn main() -> Result<(), Report> {
    #[cfg(feature = "capture-spantrace")]
    install_tracing();

    color_eyre::install()?;

    read_config()
}

#[cfg(feature = "capture-spantrace")]
fn install_tracing() {
    use tracing_error::ErrorLayer;
    use tracing_subscriber::prelude::*;
    use tracing_subscriber::{EnvFilter, fmt};

    let fmt_layer = fmt::layer().with_target(false);
    let filter_layer = EnvFilter::try_from_default_env()
        .or_else(|_| EnvFilter::try_new("info"))
        .unwrap();

    tracing_subscriber::registry()
        .with(filter_layer)
        .with(fmt_layer)
        .with(ErrorLayer::default())
        .init();
}

#[hooq(color_eyre)]
#[instrument]
fn read_file(path: &str) -> Result<(), Report> {
    info!("Reading file");
    std::fs::read_to_string(path).map(drop)
}

#[hooq(color_eyre)]
#[instrument]
fn read_config() -> Result<(), Report> {
    read_file("fake_file")
        .wrap_err("Unable to read config")
        .suggestion("try using a file that exists next time")
}
実行結果
$ RUST_LIB_BACKTRACE=full cargo run -q
2025-12-07T19:19:25.384248Z  INFO read_config:read_file{path="fake_file"}: Reading file
Error: 
   0: [src/main.rs:13:5]
        13>    read_config()
          |
   1: [src/main.rs:44:5]
        44>    read_file("fake_file")
        45|        .wrap_err("Unab..nfig")
        46|        .suggestion("try ..time")
          |
   2: Unable to read config
   3: [src/main.rs:38:5]
        38>    std::fs::read_to_string(path).map(drop)
          |
   4: No such file or directory (os error 2)

Location:
   src/main.rs:38

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

   0: project::read_file with path="fake_file"
      at src/main.rs:35
        33 │ 
        34 │ #[hooq(color_eyre)]
        35 > #[instrument]
        36 │ fn read_file(path: &str) -> Result<(), Report> {
        37 │     info!("Reading file");
   1: project::read_config
      at src/main.rs:42
        40 │ 
        41 │ #[hooq(color_eyre)]
        42 > #[instrument]
        43 │ fn read_config() -> Result<(), Report> {
        44 │     read_file("fake_file")

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                                ⋮ 4 frames hidden ⋮                               
   5: <E as eyre::context::ext::StdError>::ext_report::h579e73a738e9c80c
      at /home/namn/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/eyre-0.6.12/src/context.rs:26
        24 │             D: Display + Send + Sync + 'static,
        25 │         {
        26 >             Report::from_msg(msg, self)
        27 │         }
        28 │     }
                                 ⋮ 1 frame hidden ⋮                               
   7: project::read_file::hc03983ead05337e6
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-usage/project/src/main.rs:38
        36 │ fn read_file(path: &str) -> Result<(), Report> {
        37 │     info!("Reading file");
        38 >     std::fs::read_to_string(path).map(drop)
        39 │ }
        40 │ 
   8: project::read_config::h918a55c09a4a20e6
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-usage/project/src/main.rs:44
        42 │ #[instrument]
        43 │ fn read_config() -> Result<(), Report> {
        44 >     read_file("fake_file")
        45 │         .wrap_err("Unable to read config")
        46 │         .suggestion("try using a file that exists next time")
   9: project::main::ha2bf25fdcdfcf12e
      at /home/namn/workspace/qiita_adv_articles_2025/programs/hooq-color-eyre-usage/project/src/main.rs:13
        11 │     color_eyre::install()?;
        12 │ 
        13 >     read_config()
        14 │ }
        15 │ 
  10: core::ops::function::FnOnce::call_once::h79582e7ef8b22a23
      at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250
       248 │     /// Performs the call operation.
       249 │     #[unstable(feature = "fn_traits", issue = "29625")]
       250 >     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
       251 │ }
       252 │ 
  11: std::sys::backtrace::__rust_begin_short_backtrace::h8748ba3420932fa5
      at /home/namn/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:158
       156 │     F: FnOnce() -> T,
       157 │ {
       158 >     let result = f();
       159 │ 
       160 │     // prevent this frame from being tail-call optimised away
                                ⋮ 14 frames hidden ⋮                              

Suggestion: try using a file that exists next time

Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.

筆者「ログマシマシニンニクアブラプログラム完成...🤓」

まとめ

というわけでhooqcolor-eyreクレートと合わせてみる例を紹介しました!

...皆さんはどちらの方法でバックトレースを得るのが好みでしょうか...?今回併用する例を書いてみたのは「どっちが良い」ということを白黒はっきりさせたかったのではなく、あくまでもhooqがどれぐらい他のクレートと遜色ないかや、新しい選択肢を提供できているかを試したかったためです。体裁的な意味ではそれらしいのではないでしょうか...?

hooqの立ち位置というものはもちろんcolor-eyreとのそれを確認したところでまだ不明瞭な部分が多いです。とりあえずanyhow・color-eyreについては確認できたので、次はtracingとの詳細比較をやってみたいですね。

ここまで読んでいただきありがとうございました!

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?