Rust's Two Kinds of 'Assert' Make for Better Code

submited by
Style Pass
2023-03-17 08:30:10

My own opinion on assertions has shifted over the years, from "I don't see the point" to "use them sparingly" to "use them as much as possible". That last shift is largely due to Rust having two kinds of "assert" statement – assert and debug_assert – which has allowed me to accurately express two different kinds of assertions, largely freeing me from performance worries. If you come from a language that only has one kind of assert statement, this distinction can seem pointless, so in this post I want to briefly explain why it helped shift my thinking. Background

Let me quickly define what I mean by an "assert": it's a programming language statement that checks a property and causes a crash if that property does not hold (conventionally called a "failing assert"). For example, if I have a Python program with a list of people's ages and calculate the minimum age, I might want to check that the youngest person doesn't have a negative age: ages = [ ... ] youngest = min (ages) assert (youngest >= 0 ) If ages contains a negative value – or if min doesn't work correctly! – the assert will fail and cause a run-time exception: Traceback (most recent call last): File "/tmp/t.py", line 3, in assert(youngest >= 0) AssertionError In other words, writing assert is roughly equivalent to: ages = [ ... ] youngest = min (ages) if not (youngest >= 0 ): raise AssertionError In practise, asserts are mostly used to check assumptions about a program's state — in this case, that at no point has a negative age entered into the system.

There are two major reasons why I might want to check this particular assumption. First, I might have written subsequent code which will only execute correctly with non-negative youngest values: I want to prevent that subsequent code from executing if that property is violated. Second, the assert both documents and checks the property. In other words, I could just have written a comment: ages = [ ... ] youngest = min (ages) # youngest must be non-negative or bad things will happen below ... That comment accurately describes the program's assumption, but if the assumption is incorrect – perhaps because another part of the program uses -1 to mean "we don't know how old this person is" – the threatened "bad things" will occur. If I'm lucky, the effects will be relatively benign, and perhaps even invisible. But, if I'm unlucky, genuinely bad things will occur, ranging from odd output to security vulnerabilities.

Leave a Comment