Expand description
§TL;DR
Why have separate macros for assert_eq and assert_ne (and assert_gt etc. with other crates) when you can
just get the same output with assert!(a == b) (or assert!(a != b), assert!(a > b), …)? This crate provides a
single assert! macro that analyzes the expression to provide more detailed output on failure.
§Introduction
Rust’s standard library provides the assert, assert_eq and assert_ne macros. There are however some
inconveniences with these, like how there are no specialization for other inequalities, like assert_ge for >=
etc, or how the names only differ in one or two letters (assert_eq, assert_ne, assert_ge, assert_gt, …)
and are thus easy to mix up at a glance.
The main reason for not adding more macros is that they can be represented just fine with assert!(a >= b), so
there is no need for a separate macro for every use case.
But that begs the question: Why do we have assert_eq and assert_ne in the first place?
The practical reason: assert_eq!(a, b) provides better output than assert!(a == b):
let x = 1;
let msg = catch_panic!({ assert!(x == 2); });
assert_eq!(msg, "assertion failed: x == 2");
let msg = catch_panic!({ assert_eq!(x, 2); });
assert_eq!(msg, "assertion `left == right` failed
left: 1
right: 2"
);As you can see, assert_eq is able to provide detailed info on what the individual values were.
But: That doesn’t have to be the case. Rust has fancy-pants macros, so we can just
make assert!(a == b) work the same as assert_eq!(a, b):
let x = 1;
let msg = catch_panic!({ one_assert::assert!(x == 2); });
assert_eq!(msg, "assertion `x == 2` failed
left: 1
right: 2"
);And now we can expand this to as many operators (and even expressions!) as we want.
§Examples
let x = 1;
let msg = catch_panic!({ one_assert::assert!(x > 2); });
assert_eq!(msg, "assertion `x > 2` failed
left: 1
right: 2"
);
let msg = catch_panic!({ one_assert::assert!(10 <= x); });
assert_eq!(msg, "assertion `10 <= x` failed
left: 10
right: 1"
);
let msg = catch_panic!({ one_assert::assert!(x != 1, "x ({}) should not be 1", x); });
assert_eq!(msg, "assertion `x != 1` failed: x (1) should not be 1
left: 1
right: 1"
);
let s = "Hello World";
let msg = catch_panic!({ one_assert::assert!(s.starts_with("hello")); });
assert_eq!(msg, r#"assertion `s.starts_with("hello")` failed
self: "Hello World"
arg 0: "hello""#
);§Limitations
- Several Components need to implement
Debug- The macro will take whatever part of the expression is considered useful and debug print it. This means that
those parts need to implement
Debug. - What is printed as part of any given expression type is subject to change, so it is recommended to only use
this in code where pretty much everything implements
Debug.
- The macro will take whatever part of the expression is considered useful and debug print it. This means that
those parts need to implement
Debugprinting might happen even if the assertion passes- Because this macro prints more than just the two sides of an
==or!=comparison, it has to deal with the fact that some values might be moved during the evaluation of the expression. This means that the values have to be printed in advance. - Specifically, comparisons work as usual, but every other operator that has special output (e.g.
a+b,foo(a,b),arr[a], …) has its arguments debug-printed in advance. - Consequence: You might not want to use this macro in performance-critical code.
- Note however, that the expression and each part of it is only evaluated once, and the fail-fast behavior
of
&&and||is preserved.
- Because this macro prints more than just the two sides of an
§Changelog
See Changelog.md
§License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Macros§
- assert
- The main macro that is used to check a condition and panic if it is false.