Assertions in Rust Assert Eq Matches and Custom Messages
assert, assert_eq, assert_ne, matches and debug_assert, with custom failure messages, float comparison, and what each prints. Includes a helper for readable domain assertions.

A failing assertion should tell you what went wrong without opening the debugger. Rust's assertion macros help by printing values, and a custom message supplies the rest. The common mistakes are asserting a boolean where assert_eq! would show the values, and comparing floats exactly. This post covers the macros and a small helper for domain checks. It is part of the testing in Rust guide, and goes with unit tests.
Rust 1.90, edition 2024.
The macros
#[derive(Debug, PartialEq)]
enum State { Idle, Running { pid: u32 } }
#[test]
fn basics() {
let items = vec![1, 2, 3];
assert!(!items.is_empty(), "list should not be empty");
assert_eq!(items.len(), 3);
assert_ne!(items[0], items[1]);
let s = State::Running { pid: 42 };
assert!(matches!(s, State::Running { pid } if pid > 0));
}All four pass. Prefer assert_eq!(items.len(), 3) over assert!(items.len() == 3): on failure the first prints both numbers, and the second says only that the condition was false.
What a failure prints
#[test]
fn shows_both_sides() {
let total = 2 + 2;
assert_eq!(total, 5, "arithmetic broke for input {}", 2);
}thread 'shows_both_sides' panicked at src/lib.rs:4:5:
assertion `left == right` failed: arithmetic broke for input 2
left: 4
right: 5Order is conventional: the value under test on the left, the expected value on the right. The message after the two values is formatted like println!. Use it to state the cause when the values alone do not say what they represent.
Enums and errors
assert_eq!(result, Err(MyError::NotFound)) requires PartialEq and Debug on the error type. Many error types hold std::io::Error and cannot derive PartialEq. Then use matches!:
assert!(matches!(parse("x"), Err(ParseError::BadDigit { .. })));A failure of matches! prints only "assertion failed". For better output, let err = parse("x").unwrap_err(); assert!(matches!(err, ParseError::BadDigit { .. }), "got {err:?}");.
Floats
fn close(a: f64, b: f64) -> bool {
(a - b).abs() < 1e-9
}
#[test]
fn sums_are_close() {
let sum: f64 = [0.1, 0.2, 0.3].iter().sum();
assert!(close(sum, 0.6), "sum was {sum}");
assert_ne!(sum, 0.6);
}The exact comparison fails, since 0.1 + 0.2 + 0.3 is 0.6000000000000001 in binary floating point. The approx crate provides assert_relative_eq! with relative and absolute tolerances, which is better than a hand-picked epsilon when magnitudes vary.
A helper for domain checks
fn assert_sorted<T: PartialOrd + std::fmt::Debug>(v: &[T]) {
for pair in v.windows(2) {
assert!(pair[0] <= pair[1], "not sorted at {:?} then {:?} in {:?}", pair[0], pair[1], v);
}
}A named helper turns a repeated check into a one-line assertion with a message that says what was violated. Put #[track_caller] on it so a failure points to the calling test's line and not to the helper.
debug_assert
debug_assert! checks internal invariants in your library code during tests and development, and costs nothing in release. Do not use it to validate input.
Limits
Assertions panic, so the first failure stops the test, and later checks do not run. Collect several results and assert once if you need to see them all. For large structured values, a snapshot is clearer: see snapshot tests with insta. The macros are documented in the standard library for assert_eq.


