|
|
|
|
|
by nickmqb
2331 days ago
|
|
Agreed. Non-null pointers also come with their own set of problems: - Increased language complexity. Initialization of (arrays of) structs with non-null pointer members is more complicated because there is no straightforward "default" value that can be used. - Performance tradeoffs. Initialization of arrays and other containers is more costly for non-null pointers (or structs containing them) because we cannot simply zero out a block of memory. - In memory unsafe languages it is possible for a non-null pointer to become null if its memory is overwritten (e.g. in custom allocator scenarios (also mentioned by the author of the article), interop scenarios, etc.). The type system now makes a false guarantee, and it becomes possible for a "non-null" pointer to trigger a null pointer crash. (It's worth mentioning that enums generally have a similar problem). - I'd like do a more rigorous evaluation on this, but my gut feeling is that most null pointer bugs that I've encountered usually happen in situations where the pointer would have to be declared as nullable anyway, because I'm using null to represent a possible state. |
|
I'll respond to each in turn:
- language complexity for arrays of nullable things
In rust you can easily type 'let v: Vec<Option<&MyStruct>> = vec![None; 10]'
Having a vector of 10 'None' option types is no more difficult than anything else. Also, Option<T> implements the default trait. It defaults to 'None.
- Performance
Rust's 'Option<Box<T>>' takes up just as much space as 'Box<T>'. The null value for the pointer is used as the None type for the option. In that case it's a zero-sized zero-overhead type. You can read about that here, and in various other places: https://doc.rust-lang.org/std/option/#options-and-pointers-n...
- In memory unsafe languages it is possible for a non-null pointer to become null
Yeah. In memory unsafe languages. Don't use one of those. In Rust, Haskell, etc the Option type can't lie like that without explicitly opting in to memory unsafety. Which hardly anyone does.
- my gut feeling is that most null pointer bugs that I've encountered usually happen in situations where the pointer would have to be declared as nullable anyway
Null pointer exceptions happen when a pointer is nullable, but some location forgets that it is. If the type-system encodes this in the form of Option<T>, it's impossible to forget that. If I have an 'Option<String>' and I have a function that expects a 'String' (but not a null string), with the option type I know I have to do something like 'call_func(val.unwrap_or(""))', or I have to match, or I have to do 'let Some(val) = optional_val { call_func(val) }' before I can use that thing.
The fact that I have to do a null-check is built into the language and the compiler yells at me if I forget about it.
Being able to encode in the type-system that null should encode some valid state (e.g. 'None' in the option type) vs null-able pointers, where you use pointers that may be null even when you don't want nulls and the compiler can't check you work.. It's night and day difference.