Are assert!() and assert_eq!() removed (like in C/C++) during release builds or can I use it in release code for error checking? I'm using it to verify the bounds on some unsafe code for reading / writing from / to a byte stream. I'm trying to work out if I should convert the assert_eq!() to a an if(..) { panic!() } equivalent instead.
So in short, Quality Assurance. I was hopeful someone could point me to a written guarantee.
I did test it in release and it did work. However well engineered languages and libraries have rules not assumptions. I want to know the rule and could not get it through the documentation of the assert_eq!() macro. Thus I felt better getting a second opinion from a more experienced rust developer.
For anyone reading this in future; the assert_eq!() and assert_ne!() macro documentation does not state it, but the assert!() macro documentation stipulate the rule:
Assertions are always checked in both debug and release builds, and cannot be disabled. See debug_assert! for assertions that are not enabled in release builds by default.
The only assumption that remains is that all macros stating with assert are colloquially referred to as "Assertions" and are persistent across build types; excluding every assert starting with debug. Since the functions are core to my software, I have already change my code to using assert() instead of assert_eq!(), completely removing the risk of misinterpretation and providing direct traceability to the above statement. I generally prefer not forgoing a written guarantee for a self derived assumption. In my mind, professional engineering 101, i.e. hierarchy of controls, the best control is elimination not doing extra work to manage it.
By the way this is unsound if T is not valid for all byte patterns (for read_native) or if it T contains padding/uninitialized bytes (for write_native)
For context, I'm using the functions to send data over a named pipes between two processes on the same host (not over the network between different hosts or code versions). I simply want to copy the memory from one memory space to another. Are you specifically referring to attack vectors using the padded regions for memory infiltration and exfiltration between memory spaces? Otherwise I'm not sure I know what you mean.
arbitrary T can contain pointers or have an alignment larger than 1. Reading from an unaligned pointer is UB.
So the functions should be unsafe and have preconditions. Or you can use crates like bytemuck which have traits for types that are safe to convert from/to arbitrary bytes.
I though that the Sized trait ensured that the size must be know at compile time (i.e. the type can not contain dynamically sized contents such as pointers?) and the Copy trait ensures that the data can be shallow copied. Is this not true?
I did not explicitly think about aligned types, but that makes sense. I'm reading the docs about it now, thanks for the direction.
I though that the Sized trait ensured that the size must be know at compile time (i.e. the type can not contain dynamically sized contents such as pointers?)
Pointers and references are sized, fat pointers can point to dynamically sized data, but the pointer itself has a known size.
and the Copy trait ensures that the data can be shallow copied. Is this not true?
Copy does not ensure that arbitrary bit patterns are valid for a type. 0x03 is not valid for a bool, but it's valid for u8.
Pointers and references are sized, fat pointers can point to dynamically sized data, but the pointer itself has a known size.
I see, I was worried you were going to say something like that. Thank you, I will have a look at preconditions.
Copy does not ensure that arbitrary bit patterns are valid for a type. 0x03 is not valid for a bool, but it's valid for u8.
This makes sense, but I'm not sure this is a problem that the language can solve. For example if the data is valid for a type, but it's not the real data, it will just mask the fault. The only time I can see this happening is if the code is broken or the memory is corrupted. In either case the only real way to deal with it would be error testing, detection and correcting algorithms or hardware (ECC RAM, Voters, etc).
Generally all documented functionality applies both in debug and release builds unless stated otherwise.
assert_eq checks equality in all builds.
If the input data is valid but incorrect, you will get an incorrect but predictable behavior. If the data is invalid, you will get undefined behavior. This is what the unsafe marker on the function distinguishes: it marks possible undefined behavior.
If the input data is valid but incorrect, you will get an incorrect but predictable behavior. If the data is invalid, you will get undefined behavior. This is what the unsafe marker on the function distinguishes: it marks possible undefined behavior.
The (Rust) language declares that putting anything other than 0 or 1 in a bool is undefined behavior. If you do that, you won't just "mask the fault", but open the door to all sort of time-travelingnasal daemons.
A way to address this in Rust is to get help from the type system in the form of traits. This is why the bytemuck crate has the AnyBitPattern trait, so that the compiler can statically check that you're not going to cast a byte to a bool.