Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

They don't solve problems created by C, they just solve problems C doesn't. The problems still exist, and sometimes the high-level compiler even picks the wrong solution. Learning C helps you understand when and why this has happened.


Arguably those problems did not exist before the C-era as software wasn't large or complicated enough. C++ directly attempts to improve over C by providing new mechanisms to solve problems encountered by C developers every day.

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.


When I see templates, I run in the opposite direction at a dead sprint. C++ has added very little of value to the language and templates are one of their worst offerings. Metaprogramming is an antipattern in C.


> Metaprogramming is an antipattern in C.

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?


You can write bad code in any language, C is no exception. We also see people persistently arguing in favor of simpler and less magical C. If you want reliable, maintainable code in any language, do not use magic. Metaprogramming is cool, no doubt about it, but it's unnecessary and creates bad code.


"Exploding" undefined behaviour is a problem created by C. Other languages don't have it, not even assembly languages.


There are many languages which have undefined behavior.

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).


If a language lets you cause undefined behaviour via FFI into C, I think it's fair to say that that remains a problem created by C.

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.


> I think it's fair to say that that remains a problem created by C.

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.


> 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?


> assembly-like unsafeness

> 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.


You are right that there's no inherent need for UB. However, we made the choice to have it.

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.


> If a language lets you cause undefined behaviour via FFI into C,

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.


> FFI isn't into "C", it's into your operating system's binary format.

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.


Rust does not have a spec, so all Rust behavior is undefined.


This is literally true but also overly reductive; we do make certain kinds of guarantees, even if there isn't a full specification of the entire language yet.

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.


In my opinion as an onlooker of Rust, it seems more interested in shiny features and breaking the language every month than in becoming stable and writing a spec. It's far from replacing C in this regard.


What breaks in the language every month? That’s not the experience our users report. There are some areas where we do reserve the right to tweak things, but we take great care to ensure that these breakages are theoretical, rather than an actual pain. We have large (hundreds of thousands of LOC, and maybe even a few 1MM ones) code bases in production in the wild now, we cannot afford to break things willy-nilly.

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.


The language doesn't break as much today, but what constitutes "idiomatic" Rust is constantly changing. I don't use Rust but I spend a lot of time with people who do and am echoing what I've heard from them, and seen for myself as a casual user of Rust software and occasional drive-by contributor.

It doesn't have to be a monumental task. Rust is simply too big, and getting bigger.


Thanks. That’s much more reasonable, IMHO.

Specs are always a monumental task. The first C spec took six years to make in the first place!


But C never set out to be what it is today. It was a long time before anyone thought a spec was worth writing. For a language with the stated design goals of Rust, a specification and alternative implementations should be priority #1. You can't eliminate undefined behavior if you don't define any!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: