Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/shanecelis/dbg_if
dbg! in the loop without terminal woes.
https://github.com/shanecelis/dbg_if
Last synced: about 2 months ago
JSON representation
dbg! in the loop without terminal woes.
- Host: GitHub
- URL: https://github.com/shanecelis/dbg_if
- Owner: shanecelis
- License: apache-2.0
- Created: 2024-04-26T06:20:15.000Z (8 months ago)
- Default Branch: master
- Last Pushed: 2024-05-16T02:03:57.000Z (7 months ago)
- Last Synced: 2024-09-17T06:41:40.952Z (3 months ago)
- Language: Rust
- Size: 50.8 KB
- Stars: 7
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# dbg_if
![Maintenance](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)
[![CI](https://github.com/shanecelis/dbg_if/actions/workflows/ci.yml/badge.svg)](https://github.com/shanecelis/dbg_if/actions)
[![crates-io](https://img.shields.io/crates/v/dbg_if.svg)](https://crates.io/crates/dbg_if)
[![api-docs](https://docs.rs/dbg_if/badge.svg)](https://docs.rs/dbg_if)`dbg!` in the loop without terminal woes.
## Summary
The macro [`dbg_once!`] only prints its value the first time.
```rust
use dbg_if::dbg_once;
for i in 0..10 {
dbg_once!(i); // Outputs: [src/lib.rs:9:9] x = 0
}
```The macro [`dbg_if_ne!`] only prints changed values.
```rust
use dbg_if::dbg_if_ne;
fn f(x: u8) -> u8 {
dbg_if_ne!(x, u8)
}
f(1); // Outputs: [src/lib.rs:58:9] x = 1
f(1); // No output.
f(2); // Outputs: [src/lib.rs:58:9] x = 2
```The macro [`dbg_if_hash_ne!`] only prints on changed hash values.
```rust
use dbg_if::dbg_if_hash_ne;
let mut s: String = "hello".into();
fn f(x: &str) -> &str {
dbg_if_hash_ne!(x)
}
f(&s); // Outputs: [src/lib.rs:37:9] x = "hello"
f(&s); // No output.
s.push('!');
f(&s); // Outputs: [src/lib.rs:37:9] x = "hello!"
```The sister macros [`once!`], [`was_ne!`], and [`was_hash_ne!`] return true instead
of printing.Finally the macro [`dbg_if`] provides a kind of drop-in replacment for
[`dbg`](std::dbg) if that is your preference.```rust
use dbg_if::dbg_if as dbg;
let mut x: u8 = 0;
fn f(x: u8) -> u8 {
dbg!(x + 1);
dbg!(x + 2, Once);
dbg!(x + 3, IfNe, u8);
dbg!(x + 4, IfHashNe)
}x = f(x);
// Outputs:
// [src/lib.rs:10:9] x + 1 = 1
// [src/lib.rs:11:9] x + 2 = 2
// [src/lib.rs:12:9] x + 3 = 3
// [src/lib.rs:13:9] x + 4 = 4
x = f(x);
// Outputs:
// [src/lib.rs:10:9] x + 1 = 1
```### Feature "float"
If the feature "float" is enabled, these macros are available:
- [`abs_diff_ne_args!`] accepts `epsilon` argument,
- [`relative_ne_args!`] accepts `epsilon` and `relative_max` arguments,
- and [`ulps_ne_args!`] accepts `epsilon` and `ulps_max` arguments.These can be given as the third argument to [`was_ne!`] or [`dbg_if_ne!`]. See
the [`approx`] crate for more details.```rust
#[cfg(feature = "float")]
{
use dbg_if::{dbg_if_ne, abs_diff_ne_args};
fn f(x: f32) -> f32 {
dbg_if_ne!(x, f32, abs_diff_ne_args!(epsilon = 1.0))
}
f(1.0); // Outputs: [src/lib.rs:42:9] x = 1.0
f(1.5); // No output.
f(2.0); // No output.
f(2.1); // Outputs: [src/lib.rs:42:9] x = 2.1
}
```## Goals
- Ease debugging inspection without resorting to a debugger.
## Motivation
```rust
fn f(x: u8) -> u8 {
dbg!(x) + 1
}
assert_eq!(f(1), 2);
```The `dbg!` macro is great. It's like being able to add a probe right into your
code without disturbing everything since it works on expressions and lets them
"pass thru." For straight shot code, it is perfect.### But Not In Loops
```rust
fn f(x: u8) -> u8 {
let mut accum = 0;
for i in 0..100 {
accum += dbg!(x);
}
accum
}
``````text
[src/main.rs:59:18] x = 1
[src/main.rs:59:18] x = 1
[src/main.rs:59:18] x = 1
...^C
```For code in tight loops, however, `dbg!` leaves something to be desired. The
terminal screams, "x = 1" again and again. There has got to be a better way.### Can We Do Better?
Yes! Let's take note of the value at the call site—with a static atomic
variable—and instead of spamming the terminal with the same information, let's
only emit information when it has changed with [`dbg_if_ne!`].```rust
use dbg_if::dbg_if_ne;
fn f(x: u8) -> u8 {
let mut accum = 0;
for i in 0..5 {
accum += dbg_if_ne!(x, u8);
}
accum
}
f(1); // Outputs: [src/main.rs:59:18] x = 1
```### But I Have Non-Atomic Values?
That's fine. Can they be hashed? Because hashes can be stored in an `AtomicU64`
at the call site. Just use [`dbg_if_hash_ne!`].## Tests
Some tests require a particular setup in order to run successfully. A couple of
aliases have been placed in `.cargo/config.toml` to run these tests.- `cargo test` runs the `was*` tests.
- `cargo test-output` runs above and the `dbg*` tests which verify its output on
stdout.
- `cargo test-all` runs above and the float features.If you see errors that say, "Redirect already exists," that's because some tests
check the stdout and cannot be run multi-threaded. Use `cargo test-output` to
run them with the right arguments.## License
This crate is licensed, at your option, under either
- the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) or
- the [MIT license](http://opensource.org/licenses/MIT).### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.## Acknowledgments
Thank you to [Philipp Oppermann](https://github.com/phil-opp) for his crate
[`once`](https://github.com/phil-opp/rust-once). I initially thought I'd only
write `dbg_once!` and submit a PR. But once I got going I realized `dbg_if_ne!`
would be useful too and these are all require `std`; `once` is a `no_std` crate.
So `dbg_if` is inspired and informed by `once` but it actually doesn't share
any code with `once`.