> a misguided optimization of strcmp could do "a == b || really_strcmp(a, b)",
In practice, that's less of a risk than:
char* x = function_that_might_return_null_and_error();
dangerous_function();
if (x == NULL) {...}
Or code for which pointer identity is relevant (e.g. accessing slices/subparts of a data structure): char* x = returns_null_or_a_slice_pointer(data_structure);
char* y = other_function_that_returns_null_or_a_slice_pointer(data_structure);
if (x == y) { puts("you're fucked now!"); }You can always write:
x : Result<string, err> = function...
dangerous_function();
switch(x) {
Ok() ...
Error() ...
}
There's no difference between result and null other than readability (there's a mechanical transformation between the two, where you use unwrap() in rust on an unchecked null dereference, and you read errno in C instead of looking inside the Err() result variant).To be clear, I strongly prefer Result<> because of the readability, but any dumb thing you can write with NULL + errno can be written with Result (and vice versa).
Totally! I threw that first example in because it's the poster child for things (checked) exceptions solve, and I didn't want to unfairly favor result unions (which I also prefer).
> You have to handle NULL in C, or your program crashes
But it crashes at runtime, so such a bug can exist for years with nobody noticing.
The existence of such bugs is what motivated the musl developers to return "*" instead of NULL.
Also, in things like rust you can just do result.unwrap() if you want. In general, language constructs don't really save you from bad programmers.