I've been dismissing these "C is dead" posts for awhile, but now I'm wondering why Nim/Zig/Rust aren't superseding C entirely for new development. It seems like embedded development and FFI libraries are still all C.
My theory is that making a language is easy, but giving it good developer ergonomics and ecosystem is hard. Getting a C program to compile is still, in 2021, a pain in the butt. Do you use CMake, Scons, Ninja, Autotools? Do you need to compile on Windows/Mac? Where are the includes and libraries? This is a wild goose chase every time. How can new languages compete if the C ecosystem is barely usable after 50+ years of "progress"?
The point is that the proposal does not fully address the issue.
1. It does not define the behavior when the bounds is exceeded. When using the '-Warray-bounds' option (with gcc) the compiler does its best to detect at compile time boundary violations, but it does not add bounds check at runtime since it has no behavior to apply to this event. The remaining bounds violations are therefore ignored.
2. It does not cover the allocation of such arrays (or fat-pointer).
Given the description it could look like a VLA or something like that for heap allocated arrays:
size_t size = 3;
int (*arr)[size] = malloc(sizeof(*arr));
I doubt that many developers will do so.
In the end, my personal opinion is in favor of this kind of feature in modern languages.
But C is a specific language that addresses the need for a language that has fine control over memory and this kind of feature is not mandatory for this type of language (But maybe it's worth it? As long as it completely addresses the issue).
This problem makes me wonder:
Isn't the real mistake to use C (which is a language that allows a fine control over memory) for large applications containing a lot of logic (like a kernel)?
The C language (or equivalent) will always be useful for low-level software interfaces with hardware that use memory in an unconventional way. But languages that contain a lot of logic have other constraints that are not necessarily compatible.
We can also combine these two types of languages into one, Rust for example distinguishes between these two worlds with the keyword unsafe.
Walter's point is by even C's own standards, the behavior of automatically having an array be treated as a pointer when it is passed to a function is unexpected and a mistake.
Given he authored the D programming language, he is absolutely aware of the benefits of managing memory allocation, runtime checks for boundary overruns, etc etc...
It turns out when developing a kernel extremely fine, granular control, over memory is very important. And there are a lot of complexities that fall between the cracks of various architectures where a compiler and language having an extremely long track-record of behavior is helpful. The Linux Kernel folks have even gone as far as restricting the use of VLAs that were introduced in C99 due to unexpected side-effects and complexities that came from it:
https://www.phoronix.com/scan.php?page=news_item&px=Linux-Ki...
I'm definitely interested in RUST, but it seems like everything is viewed through a RUST looking glass these days, and all answers and roads lead to RUST.
> Given he authored the D programming language, he is absolutely aware of the benefits of managing memory allocation, runtime checks for boundary overruns, etc etc...
No doubt about that, but the proposal still does not address these issues while claiming it's an easy fix.
> It turns out when developing a kernel extremely fine, granular control, over memory is very important. And there are a lot of complexities that fall between the cracks of various architectures where a compiler and language having an extremely long track-record of behavior is helpful
I do not mean that the fine-grained control over memory is useless for developing a entire kernel, I mean that a large part of a kernel is made of logic that does not need this fine-grained control.
My point is that using unsafe fine-grained control over memory everywhere is risky, while we could keep fine-grained control over memory languages only where it's required.
> but it seems like everything is viewed through a RUST looking glass these days, and all answers and roads lead to RUST.
True, it's worth noting that Rust is not mature yet and is not easy to use
I'm excited by the Kernel maintainer's openness to include Rust, especially for driver work. But I think it's a long-road ahead until it is generally adopted.
My team in the past (which developed security device drivers), ended up splitting a lot of our observability logic into EBPF, which is basically using a verifiable memory-safe subset of C, which was pretty incredible.
9 comments
[ 2.9 ms ] story [ 24.7 ms ] threadMy theory is that making a language is easy, but giving it good developer ergonomics and ecosystem is hard. Getting a C program to compile is still, in 2021, a pain in the butt. Do you use CMake, Scons, Ninja, Autotools? Do you need to compile on Windows/Mac? Where are the includes and libraries? This is a wild goose chase every time. How can new languages compete if the C ecosystem is barely usable after 50+ years of "progress"?
For example, I know both C and Rust but still use C for my embedded projects. I am waiting until tool chains and documentation are more mature.
1. It does not define the behavior when the bounds is exceeded. When using the '-Warray-bounds' option (with gcc) the compiler does its best to detect at compile time boundary violations, but it does not add bounds check at runtime since it has no behavior to apply to this event. The remaining bounds violations are therefore ignored.
2. It does not cover the allocation of such arrays (or fat-pointer). Given the description it could look like a VLA or something like that for heap allocated arrays:
I doubt that many developers will do so.In the end, my personal opinion is in favor of this kind of feature in modern languages. But C is a specific language that addresses the need for a language that has fine control over memory and this kind of feature is not mandatory for this type of language (But maybe it's worth it? As long as it completely addresses the issue).
This problem makes me wonder:
Isn't the real mistake to use C (which is a language that allows a fine control over memory) for large applications containing a lot of logic (like a kernel)? The C language (or equivalent) will always be useful for low-level software interfaces with hardware that use memory in an unconventional way. But languages that contain a lot of logic have other constraints that are not necessarily compatible.
We can also combine these two types of languages into one, Rust for example distinguishes between these two worlds with the keyword unsafe.
Given he authored the D programming language, he is absolutely aware of the benefits of managing memory allocation, runtime checks for boundary overruns, etc etc...
It turns out when developing a kernel extremely fine, granular control, over memory is very important. And there are a lot of complexities that fall between the cracks of various architectures where a compiler and language having an extremely long track-record of behavior is helpful. The Linux Kernel folks have even gone as far as restricting the use of VLAs that were introduced in C99 due to unexpected side-effects and complexities that came from it: https://www.phoronix.com/scan.php?page=news_item&px=Linux-Ki...
I'm definitely interested in RUST, but it seems like everything is viewed through a RUST looking glass these days, and all answers and roads lead to RUST.
No doubt about that, but the proposal still does not address these issues while claiming it's an easy fix.
> It turns out when developing a kernel extremely fine, granular control, over memory is very important. And there are a lot of complexities that fall between the cracks of various architectures where a compiler and language having an extremely long track-record of behavior is helpful
I do not mean that the fine-grained control over memory is useless for developing a entire kernel, I mean that a large part of a kernel is made of logic that does not need this fine-grained control.
My point is that using unsafe fine-grained control over memory everywhere is risky, while we could keep fine-grained control over memory languages only where it's required.
> but it seems like everything is viewed through a RUST looking glass these days, and all answers and roads lead to RUST.
True, it's worth noting that Rust is not mature yet and is not easy to use
My team in the past (which developed security device drivers), ended up splitting a lot of our observability logic into EBPF, which is basically using a verifiable memory-safe subset of C, which was pretty incredible.