Hacker News new | comments | show | ask | jobs | submit login
Death to C, ++ (techcrunch.com)
39 points by janober 9 months ago | hide | past | web | favorite | 82 comments

Every aspiring programmer should learn C. They should also learn multiple dialects of ASM. Learning these things helps you better understand how computers fundamentally work, and knowing how computers work pushes you to write better code in any language. Ignoring how computers organize data can lead to inefficient code in any language.

That said, if you are writing something new, you should carefully consider whether C is the best choice before using it. If you are working within a nearly-impossible-to-replace + enormous codebase (such as the Linux kernel), it is the only option. If your project's #1 goal is performance above all else, and you are a seasoned or aspiring expert, perhaps C is the best choice. The majority of people writing software do not fall into either of these categories.

I feel like the length of the list of things "every programmer should know" is approaching infinity.

From my experience working with people, one can be an excellent programmer knowing just one language, whether it is C#, Ruby, or Java. On the other hand, I have met (too many) people who "knew" a lot but who were quite bad at putting that knowledge into practice.

I'm not sure I agree. Maybe the list of things presented by bloggers as things every programmer should know, but C would not be a new addition to such a list in any case.

I feel like the list of things "every programmer should know" tends to include "all the things I know,"in which case you may be right.

Neither C++ or Rust has any characteristic that would make it slower than C. It's all up to what is your existing codebase, and what kind of experts you have. Also, both languages can mix with C. For example, the Linux Kernel could also accept C++ code easily if they wanted, but it's a conscious decision not to do so. So, yeah, C is not the only option.

Even in an existing codebase written in C it may be possible to avoid it, if you are able to choose whether to introduce a new language for some components.

...if you are able to choose whether to introduce a new language for some components... Well, there are complexity, readability and maintenance costs associated with introducing any new dependencies into a project. And (in my opinion) these costs go in the following order, from small to high:

1. header-only library 2. compiled library 3. compiled library with type framework 4. framework 5. meta-programming framework 6. custom meta-programming framework 7. domain specific language 8. custom domain specific language 9. generic programming language 10. custom generic programming language

As a side note, increase in complexity also might increase job security of a developer. If you are after it, design your own programming language and try to deliver every project implemented in it :) Should your projects be in high demand, your skills will be in high demand as well!

Yeah, there's obviously a cost involved. It's not something I'd want to do more than once within a project.

As a younger programmer, speaking honestly, it seems to me a significant part of the motivation behind Rust evangelism has to do with distinguishing yourself against older colleagues who have 20+ years of C systems programming experience. When you find yourself in a field that's supposed to be new and fast-moving, yet you're stuck in a junior position waiting for the old guys to retire, potentially for years, as if you're a journalist or academic or something, it's not hard to see why people look to develop a different skill set.

I get the same impression, and not just from Rust. It's pretty common for most new (or newly-popular old) languages. There will always be a certain percentage of programmers who are less out to solve problems than to look or feel better than other programmers around them. What better way to do that than in a language nobody else at your company knows? Nobody to correct your style, or your use of a deprecated idiom. More glory for writing a library that didn't exist yet in that language than for improving one that already did. You get to write the early blog posts and tutorials, maybe even a book, secure yourself a permanent place at the conferences, etc. The Javascript community (like the Ruby community before it) is full of people who came from Java for those kinds of reasons. There's a similar migration path from C/C++ to Go and now Rust. A long time ago, you might have found emigres from Pascal/Modula/Ada to C.

There are many reasons to move from an older language to a newer one. There are good reasons, and there are bad reasons. I think language designers and evangelists generally do a good job of focusing on the good reasons. It's unfortunate when such advocacy tips over into FUD, but I suppose it's inevitable too. Nobody likes to see their investment devalued because their favorite vehicle to fame and glory lost out, even if the alternative really was even better.

I think there's some of that, but a large part of it the very strong user-first ideology in applied CS. Not that I'm critiquing user prioritization (at all), but the problem of categorizing what's unnecessary overhead to be handled beneath the hood by tools and what should be left up to the developer to handle is a problem that has no unique solution/is highly context-dependent.

I think younger programmers' frustrations with old, difficult (and sometimes useful) tools along with their drive to modernize everything occasionally drives them to misdiagnose something as "legacy bullshit that isn't user-oriented enough" vs. "a powerful tool which is still necessary with limited uses".

How does this all relate to language safety?

Bashing a successful language is not a way to get a new language adopted in my opinion.

I think C is a great language. I also think Rust is great but I would not bash one against the other.

This kind of evangelism is unproductive in my opinion.

Whilst I agree it is hard to consistently write safe/secure code in C for increasingly more complex code bases, and that C is no longer the best tool for the job in these cases (there are better tools/languages out there), I think the author unfairly lumps modern C++ (>C++11) in with C. Modern C++ has many of the tools necessary to mitigate many of the problems C (and <C++11) can encounter.

I do think Rust is a viable replacement for C, but not a replacement for modern C++, rather an alternative - at least for the foreseeable future.

I actually think modern C++ is less safe than C, because of the ability of destructors to be invoked invisibly and cause use-after-free, just to name one major issue.

That's not even the half of it! Start using a variety of different C++ compilers. Then there would probably be more profit running bets on what the code is going to do than there would be in the actual software.

Unless I'm mistaken, if you're using smart pointers, the memory wont be freed until the destructor has executed its code. Use after free should only be an issue if you're using raw pointers.

> Use after free should only be an issue if you're using raw pointers.

Which you are, because "this" is a raw pointer. The same applies to references and iterators, which are used everywhere. It's not feasible to use C++ without using those features.

Besides, you can get UAF without any references at all. Read/write races on a vector, for example.

The reason to use C++ is that it's reasonably widely known. "Modern C++" isn't.

I went to college less than 20 years ago where I used plenty of C++ and I've had multiple jobs since then using it (my current work is in Scala). I have no idea how to work in Modern C++ and I'm pretty confident it would take longer for me to learn than Rust.

I'd say that n-gate.com would have a field day making fun of the "Rust Evangelism Strike Force", but this almost parodies itself.

TL;DR: yet another pro-Rust rant.

C is as unsuitable for the current world ad an axe or a hammer or a knife is. All of these are tools which, when wielded without care, are likely to cause a fair bit of damage. Then again, all of them are the go-to tools for their specific applications, in the case of C that would be systems programming one level above assembly (C is "structured assembly" and should be treated as such). Good luck using Rust (et al) to program that controller with a scant few bytes of memory, or the firmware for that wifi-controller, or ...

The author does not seem to undestand how modern C++ differs from C. They are completely different languages with completely different idomatic styles. The only thing they share is some tooling.

I'd like to thank Jon Evans for motivating me to write more C.

Most of the modern web is built in C: Apache and NGINX. PHP, Python and Ruby; Linux and the BSDs.

I see the attitude to C as analogous to PHP in the web programming sphere. Everyone complains about PHP. And yet despite all the criticism to avoid PHP, the overwhelming majority of server-side web apps are written in PHP. Not Python or Ruby or Lua or some other language. Some estimates state PHP makes up 80% of server-side web code.

Why haven't these other languages displaced PHP? Because none of them can match PHPs simplicity and ease of deployment. Not Python or Ruby or Lua or any other language.

And of what of C? It's a similar scenario. Where is the fast, strongly-typed, low-memory programming language with a friendly readable syntax for low and high-level programming? Does it exist? Is Rust that language?

> Everyone complains about php

There is a famous quote by B. Stroustrup about this phenomenon.

I've heard hype around rust. Any seasoned c and/or c++ programmers use Rust now?

I write a lot of C and I've used Rust a fair amount. And to sum up my experience, I definitely think that Rust is a decent language on its own, but it has several issues that make it unsuited for the low-level code that it wants to replace. And in my opinion, this has largely come about because I don't really think many of the people involve really came from a C background, and as a result most of the stuff I see being done with it really isn't stuff that would have been written in C in the first place - and if it was, it could have already been written in several different languages (Like the coreutils). For the low-level pieces of code like kernels, Rust is lacking a lot of little things - things that, had being a C replacement been a core focus, would have never slipped through. As it is, some of these features have been added in recently, but they're in `nightly` and may very well change or get removed.

And with that, the safety of Rust is (IMO) overstated - the distinction between `unsafe` and `safe` is not a very good way to judge safety. I wrote a lot about this here[0].

All that said though, there are also lots of things I liked about Rust. In particular I found slices to be something I really wish was in C, they basically just solidify the normal way you do things in C, and it then allows the compiler do things like bounds checking. The `enum` system is also fairly nice.

[0] https://www.reddit.com/r/programming/comments/6f235k/rewrite...

I find Rust an ugly and overengineered language. C++ is getting ugly, too, but not that much. In my opinion, C is the simplest and the better for systems programming.

You can't escape the ugliness of Rust. You can write simpler and more efficient code in C++ compared to plain C.

So true. The only advantage of C compared to C++ is incomparably faster compilation - primarily due to the "abuse" of header files by C++ libraries, including STL.

What about clean syntax, simple straight forward language?

Sure, with a few caveats: 1) you can limit yourself to a reasonable subset of C++ that is just as clean and straightforward; 2) code written in C++ could look much cleaner and straightforward than it is possible to achieve in C; 3) many have argued that the C syntax is bad, especially compared to some other languages.

You can write terser code in C++ but not more efficient. Pretty much the same performance. Perhaps you meant that the programmer can be more productive; sure.

There are cases for more performance as well, e.g. C's qsort vs C++'s std::sort - for the latter, the comparison function can be inlined (e.g. default less operator).

C++ sort is faster than qsort mainly because it uses radix sort for integer data in most implementations. You can use radix sort in C, too, e.g. some benchmarks [1]

[1] https://github.com/faragon/libsrt/blob/master/doc/benchmarks...

I like the direction the lang Rust is heading, but I too find the rust lang syntax is ugly and over-engineered compared to C, C++11 and Go. Let's hope some ugly bits get deprecated.

Modern C (C99 and above) is just fine. Rust and C++ are a triumph of syntax over clarity.

Yes. It's the C replacement I've been waiting for. It's very well and carefully designed.

It took me a while to "get" Rust, but I'm now much productive in it than C. Syntax is not pretty, but it's convenient.

Now whenever I program in C I miss being able to declare which pointers are owned/borrowed (and great dependency management, etc., etc.)

The only thing I miss from C is its wide acceptance. I have to justify use of Rust, but not use of autotools :O

Like what Elixir is to Erlang, I wish there would be a NEW-LANG to Rust that has a nice syntax like Go or Swift but with the advantages of Rust, based on Rust's foundation (but with an simpler, nicer syntax).

Results in unmaintainable code for large projects.

All the fancy features sound nice, but the moment you try something large and non trivial all sorts of hacks seep into your codebase.

The solution is the same as in C++, write straightforward code and avoid terseness. But if you are at that level already you could just use C++ for a much more mature toolchain (blows away rust when it comes to compiler optimizations even when both use an LLVM backend).

In general, at some point you have to link out to the rest of the world, and that world is C/C++, so all of the borrow-checker stuff ends up being less world-changing than the strikeforce makes it out to be.

Personally, the safety aspects of rust have never been problems for me. Two biggest problems I've had developing in C/C++ over the years are A) compiler inconsistencies, B) API inconsistencies, and B isn't really C's fault. I can kind of see why Javascript is so popular--with Chrome and Node using V8, that's one less battle you have to fight.

>"the safety aspects of rust have never been problems for me." You are very likely wrong in this statement, and the fact that you don't even know how much insecure code you've shipped is exactly the problem with C/C++. The most experienced C/C++ programers in the world continue to produce security vulnerabilities, even when they're really trying to code defensively and follow best practices.

Experienced programmers produce security vulnerabilities in garbage-collected languages. Experienced programmers produce security vulnerabilities in languages that don't even do parallelism.

When someone goes on about how Rust's weak "guarantees"[0] on memory safety and data races are going to make code "secure," I don't know how to politely respond to that level of delusion.

[0] 'unsafe' blocks and the non-Rust world everything has to tie-into

How about "the safety aspects of rust have been" DWARFED by the absolute shitshow of platforms and PC hardware. If we have to redo everything to get the full benefit, we could do a damn sight better than just memory safety and race conditions.

Another bittwiddler++ language is just digging the hole deeper.

I have to point out that the very name "C++" is a security vulnerability in said language.

I learned to program in C, during the dawn of the Internet. Yes, the "I can do anything on the machine" power was fun.

But when considering all C/C++ code is completely insecure by default, it really shouldn't be used to build anything new that processes untrusted input (which is just about everything). There are a few decades worth of mature alternatives that should be considered first, plus a few newcomers like Rust and golang.

Modern C++ avoids many/most of the problems of traditional C with the use of smart pointers, standard containers and algorithms etc. If you write modern C++ in 2017 you get the speed benefits without the problems (I'd claim).

It's way too easy to do something like this in c++:

    auto v = vector<int> { 1, 2, 3 };
    auto &e = v[0]; // plan to use element later
    // ...
    // ...
    cout << e << "\n"; // boom
As a real example of this: suppose you're managing a vector of connected users. v is large enough to hold 64 user structs. e is a reference to the last user who produced an event. If a 65th user connects then the vector will need to be reallocated, thereby invalidating e.

Rust saves you here. The compiler will let you have a reference to the element if the vector is marked as immutable. If the vector is marked as mutable the compiler will yell at you and you'll need to store an index instead.

Obligatory shameless SaferCPlusPlus[1] plug: It is now practical to eliminate the use of unsafe C/C++ elements (like pointers and iterators) by replacing them with safe, compatible substitutes. Using the SaferCPlusPlus library, your example code could become:

    #include "msemstdvector.h"
    // ...
    auto v = mse::mstd::vector<int> { 1, 2, 3 };
    auto e = v.begin() + 0; // 'e' is a safe iterator here
    // ...
    // ...
    cout << *e << "\n"; // no problem
Not much different from your original code.

Rust's policy of static enforcement of "exclusive mutable references" still might be preferable in many cases, but there is a practical option of using C++ in a safe(r) way.

[1] https://github.com/duneroadrunner/SaferCPlusPlus

Yes, vector is one of the most popular containers in C++ standard library and, at the same time, it is one of the most hideous. It, along with string, has such an insane potential for abuse that a programmer should always ask themselves whether it really is what they want.

>if the vector is marked as immutable

So, mark it as const here and see the same effect?

Not really the same thing. const will stop you from mutating the vector, but it won't stop you taking a reference to an element you get in a const vector.

So, if you have a const reference, you can't make this mistake in it's entirety, but you can certainly make the mistake of keeping a reference to something you got through a const pointer.

Also, marking as const is something you have to opt-in to, and there is still disagreement on the value of const.

That is a library design issue. You can certainly design a vector that is immutable by default and can only allow you to get references when immutable and push_back when mutable.

That is the point of C++, things other languages do at a language level you can easily design as a library without paying a cost if you do not use it.

This still doesn't solve the problem because there is nothing stopping you from having both immutable & non-immutable references to the same data structure. And does not enforce that there are no references hanging around when you go from immutable to mutable.

Which is precisely what Rust does.

What does cost mean here? Performance?

You do not pay a performance cost for borrowing semantics in rust.

Marking it as const will not allow you to push_back. c++ has no problem letting you maintain a reference to an element in the vector while calling push_back.

And rust will? push_back on an immutable object sounds rather strange.

Given a mutable vector:

c++ will let you push_back while holding a reference to an element of that vector. rust will not.

Unfortunately there is a vocal group in the C++ community who are very conservative and declare all modern features and the STL automatically as bad

this is great "Security tips when programming in C (2017 edition): 1) Stop typing 2) Delete what you've already typed"

What about things that are already compiled, like your os kernel? Delete those as well? Postgres? rm -Rf? What about "rm" which is written in C as well. Pro tip is to do `rm /bin/rm` as the last command?

I love the language but this kind of shallow patronising is ridiculous and it doesn't serve Rust. It's not good for anybody. Rust can do (and does) better without it.

Not only that, many languages ( their compilers/interpreters ) are written in C...

1. C is simple,elegant but not-so-productive, not-so-modern and, for sure, very very unsafe. Oldskool hardcore, trust the programmer, quake language.

2. Modern C++ is so zero-overhead template and otherwise compile-time obsessed language. Yes it can be productive, somewhat safer than C, but the language is so terribly complicated, that it really misses the point imo.

3. Rust is modern low-level language with safety agenda and zero-overhead abstractions. The type system is still hard to master. Guess this one fits low-level system programming very well.

4. We really need a low-level productive modern language with just a 'convenience' level of safety, but high degree of control over low level performance: simd, alignment, atomics, cache levels etc.

Modern C++ gives you 4. Really. Read a tour of C++ and you'll see. The language there is VERY simple.

I have been doing C++ for most of my professional life. There is nothing simple about it. Also there is no standard way to use vector instructions or cache control. Everything else is terribly complicated with templates and rvalues/xvalues.

As have I, going on 17 years now. The vast majority of programmers do not need this.

21 years of C++ here, give or take.

I haven't seen a C/C++ programmer who doesn't need that.

> there is no standard way to use vector instructions or cache control



I'm more concerned about old web frameworks and untrained engineers using them to build insecure webapps and data storage than I am about potential consumer level malware.

Over time many protections have been put in place to help mitigate against native bugs like DEP, stack cookies and ASLR. While not full proof, they make these attack vectors a lot more difficult and who knows, maybe we'll start seeing fully position independent code for all system libraries making it even more difficult.

I really need to check out Rust though and see what all the hype is about :)

“If a feature is sometimes dangerous, and there is a better option, then always use the better option.” -- Douglas Crockford

This could probably apply to anything, including a language itself.

There's in impedance mismatch in that assertion, though. Let's assume that the label "dangerous" can be applied objectively to a feature. What does it mean for another option to be "better"?

Better by what metric? The quote seems to suggest that the only metric is how "dangerous" something is, but in the real world that's just one of many trade-offs we must make. What about performance, instrumentation and debugging features, complexity of implementation, etc.

One imagines room for quite a lot of nuance in interpreting this recommendation.

Sometimes I wonder how the computing world would turn out if Pascal has won over C.

I spent a lot of time programming in Turbo Pascal, and C has always looked like a major downgrade to me. No modules, insufficient type checking, and the build system is a completely separate step with its own languages brought in (arcane command line, makefiles, project files...).

> Rust, which is, in fact, a viable C/C++ replacement

Wrong. For example I cannot easily use a C++ framework from Rust. As bindgen and other bridge approaches get better, I have hope, but to say it is a viable replacement right now is just false.

Still too much syntax. I don't mind security, but Rust defeats the purpose by drowning code in an ocean of keywords and punctuation. I'm a firm believer in encouraging rather than enforcing the right thing; languages are tools, not religions. Writing correct C++ is perfectly possible, especially if you're willing to put in a Rust-level of effort to get there.

I don't have such impression. Rust has few keywords (should that even be used as a metric?).

Rust is very serious about declaring library interfaces reliably. It's leaning towards explicit over magic, early type errors rather than errors at substitution or runtime, so there is a bit of ceremony in function declarations, especially with generics (but then they work without having to put them in headers :)

OTOH type inference, automatic-ish memory management, everything-is-an-expression, and a lot of ergonomic help around error handling make up for it.

And I really appreciate the uniform enforcement, because I write bugs exactly where I don't expect to write them.

> Writing correct C++ is perfectly possible

Unfortunately I know of literally no large-scale project worked on by a large team using industry-standard practices (i.e. not NASA levels of verification, which are economically unviable for most software) that has consistently written correct C++.

>has consistently written correct C++.

You can easily find examples for correctness modulo errors Rust would have prevented.

off topic : what's the state of swift as a system programming language ? is its automatic memory management cost too high for this usage ?

Swift is fundamentally tied to the slow reference counting-based Objective-C GC. That's a compromise that looks unappealing outside the iOS/Mac OS sphere.

Oh god... Like Rust isn't a major PITA to use...

C's a major PITA to use too, it's just less up-front about it. C++ is a little more up-front, but also has its own minefields.

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