Reviving RFC 1662: assert_lt, assert_le, assert_gt, assert_ge

assert_eq!(foo, bar) is great because the assertion formats foo and bar to show you their values if the assertion is triggered. However, assert!(foo < bar) completely sucks because of the lack of formatting.

assert_ne! was added, but for reasons I find completely baffling, assert_lt!, assert_le!, assert_gt!, and assert_ge! (RFC 1662) were rejected. I disagree with some of the arguments in that RFC that it's "trivial to define your own macros if you really want inequality ones" (these macros are not trivial, especially to non-Rust experts).

This has led to people implementing these macros themselves:

And these are only the crates I could find with a quick Google search that publicly export assert_lt! et al. macros. I'm sure there are crates that implement these macros but don't publicly export them (like my own crate I'm writing).

Can we revisit this RFC? Would people find value in adding assert_lt!, assert_le!, assert_gt!, and assert_ge! (and their debug_* counterparts)? I know I would certainly find these valuable and would use them, but I'd like to see if the winds have changed here.

An alternative to these macros would be to change assert! to parse the expression and format the message accordingly, like assert2. But that would require specialization (or magic) in order to work with left/right-hand arguments that don't implement Debug, and specialization is a long-way off from what I understand.

3 Likes

Another cool possibility would be to expose a assert_impl!(lhs, rhs, |lhs, rhs| -> bool { .. }) kind of thing, which (could do autoref specialization and) is wrapped for assert_eq! and friends. I've found myself asserting Arc::ptr_eq, ptr::eq, Iterator::eq, and similar non-operator comparisons recently, and would benefit from being able to have the assert show the difference.

Though autoref specialization is cool, std probably isn't the place to be doing it, tbh.

The main question that I don't know the answer to is if the std macro prelude is "weaker" than glob imports. These all-macros crates are designed to be used via #[macro_use] extern crate asserts;, or in the post-extern crate world, use asserts::*;. It'd be unfortunate to break users of these crates, even if it's via lifting the exact macros into std.

2 Likes
4 Likes

Thanks, @CAD97 and @jschievink! I wasn't aware of either of those. Looking closer at assert2 it looks like it does a similar hack to work around specialization. I'll have to dig into revamping assert! because this is something I might like to work on.

IMHO this is a proposition vastly preferable to a proliferation of assert_* variants, given that pattern matching on syntax is pretty much the raison d'être of macros! Improving std::assert, or adding something like assert_cmp, to do what assert2::assert already does, shouldn't be too controversial given that an std implementation is allowed to use unstable features or magic without it being a "hack". The implementation can always be updated once it's possible to write it in stable Rust.

2 Likes

I agree that a smarter assert! is better than several assert_* variants. I just didn't realize it was (1) feasible, and (2) already a generally accepted idea.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.