Using assertions

This post is a response to my recent encounters with fellow programmers who appear to me to be missing the point of assertions and fail to appreciate their usefulness. The first post I have ever written here was on assertions, I still find it good, so there is no need to repeat it; here I will only describe how I observe people treat assertions and why I believe it is wrong.

I am reviewing the following code.

rational<int> process(rational<int> x, rational<int> dx)
{
  if (!x)
    return 0;

  rational<int> ans = x / dx;
  // ...
  return ans;
}

Template rational is Boost.Rational: it implements a mathematical concept of a rational number by storing a pair of integers. I say to my colleague:
There is a chance that you will be dividing by 0 (causing UB, a crash, etc.).
He says,
No, whenever dx is 0, x is also 0 (and vice versa). This is how the program works. So the if-statement will handle it, so I am confident it never happens.
I say,
Why don’t you put an assertion then, so that everyone knows what you just explained to me, like this:

rational<int> process(rational<int> x, rational<int> dx)
{
  assert ((x != 0) == (dx != 0));
  if (x == 0)
    return 0;

  assert (dx != 0);
  rational<int> ans = x / dx;
  // ...
  return ans;
}

And he replies,
I don’t want to do this, because it might cause a crash.

— short pause —

At this point I realized something. Not sure what exactly, but I would describe this as follows. There are different kinds or strengths of perception that some event would never occur. Apparently, when you just type a division the perception that the divisor would never be zero is strong enough, to let it go unchecked. And apparently, just planting an assert, or even the possibility of planting the assert, changes this perception. It looks that some risks of undefined behaviors appear at sub-conscious level, and an assertion helps move the risk into the conscious level.

One more reason to use assertions. They make things more conscious and objective.

In the end my colleague changed the code to this:

rational<int> process(rational<int> x, rational<int> dx)
{
  if (x == 0 || dx == 0)
    return 0;

  rational<int> ans = x / dx;
  // ...
  return ans;
}

Which was not necessarily the best move. If there is a precondition to your function, you can try to make it explicit rather than turning it into a defensive if.

For another interesting story, I am reviewing the following code:

void populate(vector<Record>& vec)
{
  for (/*...*/)
    vec.push_back(/*...*/);
}

I say to my colleague,
You probably do not expect this vector to be non-empty upon entry, why don’t you put an assertion to indicate that?

He replies,
This function is only called in one place and upon a default-constructed vector, so there is no single chance this will be non-empty, so there is no point in asserting that.

— short pause —

To my knowledge, the only place where you put assertions is where you are sure that the situation never ever occurs (unless we have a bug). This is what they are for!

This entry was posted in programming and tagged , . Bookmark the permalink.

10 Responses to Using assertions

  1. Chris P. says:

    I always enjoy your blog posts. Please keep up the great work.

  2. Scott Mueller says:

    I have this problem with my colleagues as well. Better a crash than wildly undefined behaviour.

  3. Martin Moene says:

    As John Lakos states: Defensive programming (DP) means fault-intolerance!

    In: Defensive programming Done Right, Part II at CppCon 2014,

    http://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Defensive-Programming-Done-Right-Part-I
    http://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Defensive-Programming-Done-Right-Part-II

  4. Ophir says:

    Great post, as always.
    I think there’s a bug in

    assert ((x != 0) == (dx != 0));

    It fails for no reason when x is zero and dx non-zero. You should move it after the if (x==0).
    An alternative to the assert that I usually prefer is checking and throwing,unless the overhead of checking is too high.
    One concern with assert is that it makes the debug/release versions too different, and debug/release differences are very hard to debug. But of course undefined behavior is much worse.

    • This is what I wanted to assert: they are either both zero or they are both non-zero. I think the assert is ok.

      Regarding the checking of expressions of assertions, I think it deserves another post. I believe that assertions are not for checking the expressions.

      • bdfwp says:

        I also understood that you want to assert something else: “whenever dx is 0, x is also 0”
        Strictly, that would translate to: assert(x == 0 || dx != 0)

        You’re asserting that it also holds in the other direction: whenever x is 0, dx is also 0.

  5. remember kids, 1 assert is worth 5 comments :)))))))))
    something they should teach!

  6. evpo says:

    And he replies,
    “I don’t want to do this, because it might cause a crash.”

    I don’t think this remark is valid. It will not crash in production. If it crashes in a unit or integration test, is it not what we want?

Leave a reply to Scott Mueller Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.