3 ms·
I'm not sure i think those situations are comparable. If a rust func is taking an Option<t>, its essentially advertising that it can handle None values. That fe
by bawolff 4mo ago
I'm not sure i think those situations are comparable. If a rust func is taking an Option<t>, its essentially advertising that it can handle None values. That feels quite a bit different from giving a c function a null pointer and having it freak out.
- eptcyka 4mo agoYe, sure, but Rust won’t compile a `foo(std::ptr::null())`, if the function is defined as `fn foo(b: &Baz)`. C doesn’t get that luxury. That is the point of the article.
- f33d5173 4mo ago$ gcc -Wall -Werror -x c - << EOF void f(int x[static 1]){}int main(){f(0);} EOF <stdin>:1:37: error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull] 1 | void f(int x[static 1]){}int main(){f(0);} | ^ ~ <stdin>:1:12: note: callee declares array parameter as static here 1 | void f(int x[static 1]){}int main(){f(0);} | ^~~~~~~~~~~ 1 error generated. It can be done, though it usually isn't.
- saghm 4mo agoCan you do that with a dynamic array? If not, it's pretty severely limited (unless you mean that literally forbidding dynamic memory is usually not done, which I guess it's true outside of some embedded code but not a particularly meaningful statement).
- f33d5173 4mo agoThe syntax is rather confusing. This is not an array of length 1, but rather a pointer which points to a memory segment which is at least 1 integer long. In other words, any array of any length (>=1) would be a valid argument to this function. "static" here means "don't do the normal thing where you totally ignore the length of an array argument to a function (which is, like usual, really just a pointer)". "static" was chosen because the keyword was available rather than because it was a particularly descriptive name.
- eptcyka 4mo agoI can understand that this a somewhat practical solution to the problem of codebase is all C and all I have is a C compiler, so there is merit to using the static keyword here. But it is also unwieldy and used rarely, so I do not think it is comparable to Rust’s borrow rules in any meaningful sense.
- saghm 4mo agoI see. Can you use `static 0` for a non-null empty array, or does that run into issues around what it means to allocate 0 bytes? I feel like the distinction between "empty but safe to use" and "not safe to use, so you need to check first" is a pretty important part of what most of the solution look like in languages that don't have this problem. Being able to statically know that something is non-empty is nice, but it's not quite the same as being forced to check if it's valid.
- wahern 4mo ago> Can you do that with a dynamic array? Yes. #include <stdio.h> #if __has_include(<stdcountof.h>) #include <stdcountof.h> #else #define countof(a) (sizeof (a) / sizeof *(a)) #endif void foo(int n, int a[static 1][n]) { printf("sizeof *a: %zu\n", sizeof *a); printf("countof *a: %zu\n", countof(*a)); } int main(int argc, char *argv[]) { int array[argc]; foo(countof(array), &array); return 0; } $ ./foo sizeof *a: 4 countof *a: 1 $ ./foo 2 3 sizeof *a: 12 countof *a: 3 Tested using Apple clang 21.0.0 and gcc 15.2.0. The syntax for using passed VM arrays is stilted; you're operating on a pointer to an array, which can get confusing and is error prone. Because of the semantics for array passing and need for backward compatibility it's too easy to get it wrong without the compiler catching mistakes and complaining. Though, the C2y _Countof operator is required to error when used on a non-array, so using _Countof(a) instead of _Countof(*a) will fail. (GCC and clang also have warning diagnostics that work for the fallback countof macro.) And there's no way (or no easy way?) to ask compilers to inject automatic bounds checking when operating on arrays, at least outside non-production debugging modes like ASan. But C is getting there, slowly.
- eptcyka 4mo ago`int x[static 1]` isn’t exactly intuitive when one wants to define a reference to an integer. Nor is this practical given a dynamic array of integers. Or a single integer field in a struct.
- tialaramex 4mo agoBut it isn't different, that's Tony Hoare's Billion Dollar Mistake.
- __s 4mo agoIt's absolutely different: “I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.” His mistake was making _all_ reference taking functions also accept null. In Rust functions opt into None | Some This comes up with C# which must have default(T) so references default to null. In Rust there is no general default(T) that must always resolve
- tialaramex 4mo agoMy contrast was to "That feels quite a bit different". The type system in C only has Tony's nullable references, you can't say that you don't mean that because it wasn't your choice to make, it's like if some C programmers say obviously they don't mean zero when they take an integer - too bad, C doesn't have the non-zero integers (Rust does, NonZeroI32 for example is the signed 32-bit integers except zero)
- selfmodruntime 4mo agoIt is different. Handling `None` in a way that crashes your program is well defined in a Rust function. If you're using `unwrap` or `expect`, the program will crash with a stack trace and an error, instead of running into undefined behavior.
- josephg 4mo agoYes. Its also explicit instead of implicit. In rust (and typescript, swift, haskell and others), you have to opt-in to nullability. By default, functions can't take a null in place of a non-nullable value. And whether a function accepts a T or an Option<T> is part of the signature.