Is it? I program in C a lot, and null pointer dereferences are not really an issue in my experience. And any option type (which you could also have in C) does not really change the fundamental problem that you have some exceptional state you need to handle at some point and if this is happens at a point where this is not expected, this blows up - also in other languages. I also once believe that complex type systems are the answer, but over time I realized this is not really true.
Yes it is.
1) Most of pointers in real apps are non-nullable and it's nice to have enforcement from a compiler.
2) Good compilers verify you actually check nullable (optional) values have a corresponding check. In particular Zig literally forces you to unwrap value, so no unexpected state.
It's a really amazing QoL.
And no, optionality doesn't make type system any harder. Also C lacks alignment enforcement on type level and it's a real footgun which Zig also fixed. Zig has many warts but this part (optionality, alignment and slices) makes a big difference comparing to C without Rust/C++ level of type acrobatics.
> I also once believe that complex type systems are the answer, but over time I realized this is not really true.
It's suboptimal decision, you load your brain with stuff compiler should resolve for you.
Edit: ahah, just noticed your nick. Really appreciate your work on C improvements. Please ignore my yapping :) I literally know nothing comparing to you.
My worries about coding all these things into type systems is that this freezes the semantics at a time during development where one is still figuring out the ideal semantics.
In any case, I wonder what you think about my experimental maybe type? https://godbolt.org/z/MTdj81841
> My worries about coding all these things into type systems is that this freezes the semantics at a time during development where one is still figuring out the ideal semantics.
I don't get this part at all, especially in a language with static typing. It's completely normal for me to not be sure which type I need here and so I use a type alias, or if I'm even less sure I make a wrapper type so that it only has the properties I choose. So I don't see this "freezing" semantics any more than an identifier name would - we can just change it if we change our minds, no problem.
To give a comparable example from elsewhere, when I'm still feeling out the problem I often write Rust's loop, the infinite loop which is Rust's only primitive looping structure (for and so are just sugar). That doesn't mean I write only weird odd-ball loops, but when I'm not sure the raw infinite loop is fine if I decided to consume all the Widgets in this Vec<Widget> like a for-loop but it's also OK if I decided to index through it, or do something entirely different. Rust's "clippy" will prompt me to write a more idiomatic while-let or for-each loop when I've nailed down the whole thing.
I like maybe_value dereferences NULL with SIGILL and you have at least a backtrace with exact invalid access position.
Oh, you can get the information at compile time as well. https://godbolt.org/z/hTYbTE8j8 (or https://godbolt.org/z/K1M68sY3Y but this will be tricky in a larger code base)
That's the reason language/compiler should directly support optionality.
You can't fool yourself and have to handle all cases: https://godbolt.org/z/4GqMdPej3
So it seems exactly the same to me?