# \[Pre-RFC\] assert macros simplified

**URL:** <https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280>\
**Category:** libs\
**Created:** [May 21, 2017, 7:19am UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280 "2017-05-21T07:19:33Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![ishitatsuyuki](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ishitatsuyuki/32/2163_2.png) [@ishitatsuyuki](https://internals.rust-lang.org/u/ishitatsuyuki)\
**Post date:** [May 21, 2017, 7:19am UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/1 "2017-05-21T07:19:33Z")

</div>

# Problem

We have many types of assert macro just for printing more helpful messages. We only need one type of them. (Of course, we won’t get rid of debug\_assert since it’s completely a different variant.)

# Proposal

Make everything fit within the assert macro. For example, if the final operation is “==”, make it like assert\_eq. We should also expand this to ordering operators like “\<”. There are many frameworks mostly in C++ like [Catch](https://github.com/philsquared/Catch) and [Boost.Test](http://www.boost.org/doc/libs/1_64_0/libs/test/doc/html/index.html) already achieves that using macros. The problem with C++ macros is that they’re hard to write and they doesn’t always yield the desired behavior. However, Rust’s compiler (procedural) macros are very powerful, we can access the AST for precise parsing. We may also print out the immediate expressions, like [1 + 2 != 3]. This needs further discussion.

# Unresolved questions

- should we make assert! a compiler built-in? Can this done with macro\_rules?
- should we print out all values contained in the expression?

---

<div class="post-metadata">

**Author:** ![xfix](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/xfix/32/2726_2.png) [@xfix](https://internals.rust-lang.org/u/xfix)\
**Post date:** [May 21, 2017, 8:49am UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/2 "2017-05-21T08:49:31Z")

</div>

> [@ishitatsuyuki](#):
>
> Can this done with macro\_rules?

It can, but it's not pretty. macro\_rules doesn't provide access to AST, so you have to parse those things yourself.

And because this is a heavily recursive macro (I cannot really think of a way to do it without tt-munching, but perhaps there is one), it may as well reach the recursion limit. So it's probably not really an option for a standard library macro (`macro_rules!` implementation that is).

```rust
macro_rules! assert_internal {
    (normal [$($left:tt)*] && $($tail:tt)*) => {
        assert_internal!(nope $($left)* && $($tail)*)
    };
    (normal [$($left:tt)*] || $($tail:tt)*) => {
        assert_internal!(nope $($left)* || $($tail)*)
    };
    (normal [$($left:tt)*] == $($tail:tt)*) => {
        assert_internal!(eq [$($left)*] [] $($tail)*);
    };
    (normal [$($left:tt)*] $t:tt $($tail:tt)*) => {
        assert_internal!(normal [$($left)* $t] $($tail)*)
    };
    
    (eq [$($left:tt)*] [$($right:tt)*] && $($tail:tt)*) => {
        assert_internal!(nope $($left)* == $($right)* && $($tail)*)
    };
    (eq [$($left:tt)*] [$($right:tt)*] || $($tail:tt)*) => {
        assert_internal!(nope $($left)* == $($right)* || $($tail)*)
    };
    (eq $left:tt [$($right:tt)*] $t:tt $($tail:tt)*) => {
        assert_internal!(eq $left [$($right)* $t] $($tail)*)
    };

    (normal [$all:expr]) => {
        assert_internal!(nope $all)
    };
    (nope $all:expr) => {
        println!("Regular assert: {}", stringify!($all));
    };
    (eq [$left:expr] [$right:expr]) => {
        println!("Assert equal, left: {}, right: {}", stringify!($left), stringify!($right));
    };
}

macro_rules! assert_demo {
    ($($tail:tt)*) => {
        assert_internal!(normal [] $($tail)*)
    }; 
}

fn main() {
    assert_demo!(a);
    assert_demo!(a == b);
    assert_demo!(a && b == c);
    assert_demo!(a == b || c);
}

```

And all that to determine if the top AST node is equality node (yes, I know there are more operators than `&&` and `||`, but it would be just adding additional rules which would distract from the code).

---

<div class="post-metadata">

**Author:** ![repax](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/repax/32/1342_2.png) [@repax](https://internals.rust-lang.org/u/repax)\
**Post date:** [May 21, 2017, 7:38pm UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/3 "2017-05-21T19:38:56Z")

</div>

The `Catch` testing framework is just beautiful. Its assertion errors are very clear:

```php
test.cc:100: FAILED:
  REQUIRE(foo(x) >= y)
with expansion:
  3 >= 4

```

Compare this to:

```php
---- test::foo stdout ----
    thread 'test::foo' panicked at 'assertion failed: `(left >= right)`
(left: `[3]`, right: `[4]`)', test.rs:100
note: Run with `RUST_BACKTRACE=1` for a backtrace.

```

I don’t think that immediate technical reasons must limit aesthetic aspirations.

---

<div class="post-metadata">

**Author:** ![tmccombs](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tmccombs/32/1845_2.png) [@tmccombs](https://internals.rust-lang.org/u/tmccombs)\
**Post date:** [May 21, 2017, 8:05pm UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/4 "2017-05-21T20:05:04Z")

</div>

I love this. It doesn't necessarily need to be in stdlib, though. Although a really good implementation would probably require procedural macros.

> should we print out all values contained in the expression?

I think so. At least at the top level. With the current assert macros I find I frequently need to add `println!` statements to figure out what the actual value is.

---

<div class="post-metadata">

**Author:** ![xfix](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/xfix/32/2726_2.png) [@xfix](https://internals.rust-lang.org/u/xfix)\
**Post date:** [May 21, 2017, 8:31pm UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/5 "2017-05-21T20:31:12Z")

</div>

> [@tmccombs](#):
>
> Although a really good implementation would probably require procedural macros.

I don't think procedural macros are needed for a good implementation (`macro_rules!` will do). The task is essentially to figure out if there is `==`/`!=` token, and no tokens of higher precedence (I suppose you may also add an extension allowing expressions like `((a == b))`, but it's really easy to handle). Note that parenthesis/blocks don't need to be considered, as they are a single token (matched fully by `:tt`) as far language is concerned. The only issue is recursion limit, which is decided by user of a macro, and not by macro itself

---

<div class="post-metadata">

**Author:** ![ishitatsuyuki](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ishitatsuyuki/32/2163_2.png) [@ishitatsuyuki](https://internals.rust-lang.org/u/ishitatsuyuki)\
**Post date:** [May 22, 2017, 7:16am UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/6 "2017-05-22T07:16:19Z")

</div>

> [@xfix](#):
>
> I don't think procedural macros are needed for a good implementation (macro\_rules! will do).

IMO macro\_rules! is limited and slow. As long as we're bringing the feature into standard library, there's no problem using a compiler macro, which express the things more directly.

I think we can apply this on all operators except `.` (function call or struct field reference). That way we can display the most helpful debug dump as possible. Expression block (`{ sth }`) can be used to not be dump out the block.

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [May 22, 2017, 5:51pm UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/7 "2017-05-22T17:51:08Z")

</div>

I’d be happy even if it only supported basic cases (falling back to expecting `true` in hard to parse expressions), since most of the time my assert expressions are pretty simple, such as `result > 0.5`.

---

<div class="post-metadata">

**Author:** ![tmccombs](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tmccombs/32/1845_2.png) [@tmccombs](https://internals.rust-lang.org/u/tmccombs)\
**Post date:** [May 23, 2017, 5:23am UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/8 "2017-05-23T05:23:28Z")

</div>

I experimented with trying to implement something like this with `macro_rules!`.

With an implementation like:

```
macro_rules! assert2 {
    ($left:tt == $right:tt) => ({
        match (&$left, &$right) {
            (left_val, right_val) => if !(*left_val == *right_val) {
                panic!("{0} == {1} was false\n\t{0} = {2:?}, {1} = {3:?}",
                       stringify!($left), stringify!($right),
                       left_val, right_val);
            }
        }
    })
}

```

Then `assert2!( 5 == 2 + 3)` fails to compile with “no rules expected the token +”. If I change left and right to be expr instead of tt, then it still fails with "`$left:expr` is followed by `==`, which is not allowed for `expr` fragments".

Wrapping the `2 + 3` in parenthesis does work with with the `tt` version, but that is a little awkward and non-intuitive to use.

I suppose it is possible to build something that would handle arbitrary expressions, but as far as I understand it, it would have to essentially parse the expressions themselves, duplicating a significant portion of rust’s expression grammar.

---

<div class="post-metadata">

**Author:** ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)\
**Post date:** [May 23, 2017, 11:51am UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/9 "2017-05-23T11:51:20Z")

</div>

You can have `($left:tt == $right:expr)`, which matches `5 == 2 + 3`.

Of course it won’t match the other way, `2 + 3 == 5`, since expressions can contain `==`, so the parser has no way of knowing when to stop parsing `expr` and when to parse `==` in the macro. But instead of an error, there can be a generic fallback in such case:

```rust
macro_rules! assert2 {
    ($left:tt == $right:expr) => {
        match (&$left, &$right) {
            (left_val, right_val) => if !(*left_val == *right_val) {
                panic!("{0} == {1} was false\n\t{0} = {2:?}, {1} = {3:?}",
                       stringify!($left), stringify!($right),
                       left_val, right_val);
            }
        }
    };
    ($right:expr) => {assert!($right)};
}

fn main() {
    assert2!(5 == 2 + 3);
    assert2!(2 + 3 == 5);
}

```

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [March 25, 2019, 8:28am UTC](https://internals.rust-lang.org/t/pre-rfc-assert-macros-simplified/5280/10 "2019-03-25T08:28:34Z")

</div>

Closed on March 25, 2019, 8:28am UTC

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