Point is that out of context, a lot of the things modern languages provide seem superfluous. Why have objects? Why have templates, closures, or lambdas? For a novice programmer, these are many answers for questions yet to be asked.
When you come from a heavy C background and you encounter something like templates, you know EXACTLY what this is for and wish you had it years ago.
I'm as hardcore as they get C programmer and I'm having a ball with C# for this very reason.
Yet we often see passionate arguments in its favor, as for instance from Sústrik (http://250bpm.com/blog:56) and Tatham (https://www.chiark.greenend.org.uk/~sgtatham/mp/) and a person on the internet who recommends replacing LAPACK with preprocessor macros (http://wordsandbuttons.online/outperforming_lapack_with_c_me...). Would you care to comment on its enduring popularity?
C does have a lot of it, and it can be anywhere. In many languages, you can cause undefined behavior through its FFI. Some languages, like Rust, have UB, but only in well-defined places (a module containing unsafe code, in its case).
I do take your point about Rust, but I'd see that as deriving from LLVM's undefined behaviour which in turn descends from C; I'm not aware of any pre-C languages having C-style exploding undefined behaviour or of any subsequent languages inventing it independently.
That is fair, however, I don't think it's inherently due to C. Yes, most languages use the C ABI for FFI, but that doesn't mean they have to; it's a more general problem when combining two systems, they cannot track statically the guarantees of the other system.
With Rust, it has nothing to do with LLVM; it has to do with the fact that we cannot track things, that's why it's unsafe! Even if an implementation of the language does not use LLVM, we will still have UB.
I can see that any implementation of unsafe Rust would always have assembly-like unsafeness (e.g. reading an arbitrary memory address might result in an arbitrary value, or segfault). But I don't see why you would need C-style "arbitrary lines before and after the line that will not execute, reads from unrelated memory addresses will return arbitrary values" UB?
> I don't see why you would need C-style "arbitrary lines before and after the line that will not execute, reads from unrelated memory addresses will return arbitrary values" UB
This reason this happens is because we don't compile things down to obvious assembly, they get optimized. Each of those optimizations requires assumptions to be made about the the code. If you break those assumptions, then the optimizations can result in arbitrary results happening. Those assumptions determine what is and isn't UB.
Most languages just don't give the programmer any way to break those assumptions, but languages like C and Rust do. Thus, Rust will always have this 'problem' because it can/will make even more aggressive optimizations then C will, meaning badly written `unsafe` code will have arbitrary behavior and results from the optimizer if the compiler doesn't understand what you're doing.
The reason to have it is the exact same reason that the distinction between "implementation defined" and "undefined" exists in the first place: UB allows compilers to assume that it will never happen, and optimized based on it. That is a useful property, but it's a tradeoff, of course.
FFI isn't into "C", it's into your operating system's binary format. And since no two systems behave the same, it's UB however you look at it.
Most people call that format "the C ABI," and "FFI into C" is short for "FFI via the C ABI."
> And since no two systems behave the same, it's UB however you look at it.
That's not what UB means. It would be implementation defined, not undefined.
The reason that a spec is taking so long is that we're interested in making an ironclad, really good spec. Formal methods take time. C and C++ did not have specs for much, much longer than the three years that Rust has existed. We'll get there.
You are right that we are far, but that’s because it’s a monumental task, and it’s fundamentally unfair to expect equivalence from a young language. It is fair to say that it is a drawback.
It doesn't have to be a monumental task. Rust is simply too big, and getting bigger.
Specs are always a monumental task. The first C spec took six years to make in the first place!