Live data from Hacker News

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

tratt.net

31–40 of 87 posts

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

#31

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.

What C compiler would add bounds checks?

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

#32

Earlier 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.

In terms of category theory, what we would need is subtraction and division types on top of product and sum types.

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

#33
post #13

Earlier 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.

You can do it easily in any language with functions and flow control. Restarts and coroutines are just if-errs and CPS-natured code in disguise. The difference is only syntactic, and there are ways to reduce the noise to manageable levels.

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

#34

I 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.

I'd say it's more like wearing a life jacket while building and testing a ship, but not imposing to every passenger to wear one once the ship is certified and put in service.

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

#35
post #27

The 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…

I understand and respect your position.

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

#36
What about the fact that the assert causes a panic that crashes the program? Is there not a better alternative of doing your bounds checking as a first class aspect of the function and returning an error? If someone is deep into a session with your application and the cursor somehow drifts into an unknown state (which could be corrected with the “home” key or something else), for example, getting too crazy with assertions seems like a much worse result. (E.g. “assert!(is_on_screen())”)

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

#37
I 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 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

#38
post #37

I 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…

In Python, contract based programming can be only be done properly with asserts:

- 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

#39

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.

What C compiler would add bounds checks?

That was just an example. In Rust bounds checks can be eliminated in this way, but I'm sure there are similar optimisation opportunities in C code as well.

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

#40
I agree with the others here that classic assert statements are in a weird spot. You enable it when testing, in other words when the data flowing through your program is likely to be constrained and lazy. And then turn it off when your program is about to meet real data (production). There's some wrinkle to this where you might want to log certain things in production instead of aborting with an assertion failure. But let's assume failing hard and fast is preferred for now.

Basically 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.

Post reply on HN