One point I didn't see mentioned that asserts can be used by the compiler to enable certain optimisations. For instance, if you assert that an incoming index into a function is within the bounds of a vector, then during the rest of the function the compiler can elide any bounds checking.
Rust's Two Kinds of 'Assert' Make for Better Code
31–40 of 87 posts
Re: Rust's Two Kinds of 'Assert' Make for Better Code
#32Earlier quoted context omitted.
If you created a custom NonZero type, how would the Rust compiler figure out how to optimize that? How would one communicate properties/traits of a custom type (like a non-zero unit) that a compiler can leverage to optimize ( in general, for any programming language )?
In general, you need a type system that supports sets of integer values, e.g. range(2, 7) or set(3, 5, 7). Rust doesn't support that unfortunately so it has a special annotation instead to make NonZero work.
So a 32-bit integer is the product of 32 two-state bit types. Something akin to NonZero could be defined as that type minus one state, such that there are now 4294967296 - 1 representable values.
Similarly, pointer types on some machines always have some bits set to 0 due to hardware constraints. These can be represented in the type system as 2^64 / 2^n where 'n' is the number of bits that are not usable, resulting in something like 2^46 for typical CPUs. This would allow extra bits of state to be "packed in there".
This concept is more generally useful, not just for bit-packing.
For example, a database table row might be represented by a struct that contains a field for every column. But then, how to represent the result of a SELECT query!? Either you code-gen this into a new type, create that type manually, or use compiler-generated "unspeakable" type names. (Linq in C# does this.)
Or... the language could natively support type division, so you could use one struct type for "all columns" and then use division on the type to delete columns you don't need for a particular query or REST response.
There's a whole ecosystem of tools that work around the problem of not having this as a built-in feature in typical languages: AutoMapper, automap, Mapster, etc...
Re: Rust's Two Kinds of 'Assert' Make for Better Code
#33Earlier quoted context omitted.
It would be interesting if a language allowed control flow to jump between catches and exceptions with named sort of exceptions. E.g., imagine in this example that the code code throw an invalid index exception, some calling code could catch that, and supply a new index, and control flow would resume from the throw expression. This would be a complete mess, but it would be interesting nonetheless :)
You can do it easily in any language with coroutines, for example Lua.
Re: Rust's Two Kinds of 'Assert' Make for Better Code
#34I remember someone arguing that disabling assertions in prod is like wearing a life jacket in the harbor but throwing it overboard going to sea. And Moore's Law paid for them years ago.
Re: Rust's Two Kinds of 'Assert' Make for Better Code
#35The distinction between debug and release mode wrt my code (assertions, debug logging) was always confusing to me. The experience told me uncountable times that “release” (iow “prod”) is where you want debug information to be produced, cause that’s where your code meets full-blown reality. Otherwise your system silently breaks in production after few weeks, and two weeks later someone will ask you why and to repair i…
Nonetheless, I use asserts that are deactivated in release builds. The reason I do that is not because I need the speed. It's because it frees me from having to think about speed at all, when writing assertions. And that makes me write more assertions.
You could ask me, if I were to enable assertions in release builds today, what would the slowdown be? Would I even notice? And my answer would be, I don't know and I don't care. What I do know is that if assertions had been enabled in release builds from the start, then I would have written fewer of them.
Re: Rust's Two Kinds of 'Assert' Make for Better Code
#36Re: Rust's Two Kinds of 'Assert' Make for Better Code
#37The cost of a failing assertion is often incurred at some point where it is most expensive. I know this because I've worked for companies that have charged exorbitant prices for my time to diagnose these sorts of failures.
The cost of a unit test is some learning and some time. LLMs are making the time portion drive towards zero.
As a general rule, I'd say avoid littering your code with assertions. It's a crappy engineering practice.
Re: Rust's Two Kinds of 'Assert' Make for Better Code
#38I don't think I've ever in 30+ years of programming seen a runtime assertion that wouldn't be improved by doing something else instead. Writing unit tests and choosing designs that codify the constraints in the type system are two things that are obvious. The cost of a failing assertion is often incurred at some point where it is most expensive. I know this because I've worked for companies that have charged exorbita…
- You can't use the type system for some constraints.
- Asserts can be stripped in production so you don't pay the price for them.
- There is not other syntax for that.
Unfortunatly, because most devs don't know about "-O" and use asserts for things you should use an Exception for, you can't use it: https://www.bitecode.dev/p/the-best-python-feature-you-canno...
Re: Rust's Two Kinds of 'Assert' Make for Better Code
#39One point I didn't see mentioned that asserts can be used by the compiler to enable certain optimisations. For instance, if you assert that an incoming index into a function is within the bounds of a vector, then during the rest of the function the compiler can elide any bounds checking.
What C compiler would add bounds checks?
Re: Rust's Two Kinds of 'Assert' Make for Better Code
#40Basically there are three cases.
1. The performance hit of the assertion checks are okay in production
2. It has a cost but can be lived with
3. Cannot be tolerated
For number two there is some space to play with.
What I've wanted is something more fine-grained. Not a global off/on switch but a way to turn on asserts for certain modules/classes/subsection, certain kinds of checks, and so on. It would also be nice to get some sort of data about the code coverage for assertions which have lived in a deployed program for months. You could then use that data to change the program (hopefully you can toggle these dynamically); maybe there is some often-hit assertion that has a noticeable impact which checks a code path that has been stable for months. Turning it off would not mean that you lose that experience wholesale if you have some way to store that data. I mean: imagining that there is some history tool that you can load the data about this code path into. You see the data travels through it and what range it uses. Then you have empirical data across however many runs (in various places) that indeed this assertion is never triggered. Which you can use to make a case about the likelihood of regression if you turn off that assertion.
That assumes that the code path is stable. You might need to enable the assertion again if the code path changes.
Just as a concrete example. You are working with some generated code in a language which doesn't support macros or some other, better way than code generation. You need to assert that the code is true to whatever it is modelling and doesn't fall out of sync. You could then enable some kind of reflection code that checks that every time the generated code interacts with the rest of the system. Then you eventually end up with twenty classes like that one and performance degrades. Well you could run these assertions until you have enough data to argue that the generated code is correct with a high level of certainty.
Then every time you need to regenerate and edit the code you would turn the assertions back on.