Death to C
techcrunch.com
techcrunch.com
The argument is over-simplified and rather than looking to the root of the problem tries to sweep it under the rug by also ignoring the fact that many new languages have their interpreters written in C. The root issue is that the C language needs an update, an overhaul, and that once the standard has been refreshed, C will continue to be the #1 language among programmers.
Sadly, nothing can be done for C++ and to lump C and C++ together is as upsetting as calling for programmers to cease the use of C! :-)
Last thought: There needs to also be more focus on teaching programmers how to write secure C programs. Every year new exploits are released that target insecure C programming styles, and the C community should make sure programmers are being educated on how to avoid these issues. Look to subsets like MISRA C as an example and a move in the right direction; advocating secure programming/language subsets rather than changing languages ultimately makes more sense.
There are a lot of warts in the language and a lot of people in the community writing bad code. But there is a good language emerging, and plenty for low-level hackers to appreciate.
However, I have noted in my career that almost every package I regularly use and have to do something with at the build level is written in C (or Lisp). However, I do know of one project written in C++ that I respect and does great things, and that is GNU Octave.
Lambda expressions in the STL... Go figure!
Stl is similarly elegant from the narrow perspective of inlining, and getting rid of indirection that people tend to write when doing abstract data types in C. It took me a long time of hating C++ and preferring C before I was willing to see this. I still kind of prefer C but these days I am much more willing to concede that there are strengths in C++ that you can't easily match with equivalent C code.
Please; "hacker" does not mean "inexperienced amateur", as you seem to imply it means here. The typical meaning is close to the opposite:
http://www.catb.org/jargon/html/H/hacker.html
As to C++, it has undeniable positives as well as undeniable negatives (according to plenty of experienced professionals).
This is why it is unpleasant to correct objective errors on the internet.
There should be a new C that obviates the substantial parts of MISRA.
Case in point: MISRA C forbids goto statements primarily because it can mess up static analysis. Yet this rule is gratuitously followed even when no static analysis tools are used, thus yielding none of the gains that you trade off for occasionally writing ugly code.
Otherwise, you're right. The value goes out the window without enforcement, without compliance.
The rules themselves are not meaningless or without a point, but there are a lot of companies that adopt MISRA without actually having (in the sense of audit and certification) to be compliant. Instead of focusing on the point of every provision, they rigidly follow them even when not applicable.
But it can be worse, really. The gem of a coding standard we have at $work forbids not only goto, but also break, without MISRA's exception of one break per loop. And forbidding the use of goto and continue is cited as being done for readability reasons, rather than static analysis tools.
But, for myself, I notice this: Writing Rust programs (vs. writing C programs) correctly is hard!
N.B. This might be an old dog new tricks thing.
I automatically structure my programs (be they rendering engines, or computation/numerical simulations or even agent-based simulations) where allocating and freeing memory from/to pools just isn't that hard. I don't really think about it. I just throw together my code, compile it and run it.
Of course, I make many (many!) silly/stupid mistakes during the course of development, but failing to free a pointer is rarely one of them.
Now I loop back to your point. (Sorry I took the long way)
> Everyone already knows what the problems of C are, what they need to know is what the alternatives offer and how in the end you are not giving up too much.
I know what the problems of C are. I am aware of the alternatives. But the problems of C don't seem to be overwhelming enough to bother throwing away and constructing an entirely new tool-chain and support system on whatever your platform is.
That was a pretty bold statement, and I would invite a response.
I would like to think that we, as an industry, are not just implementing new language ecosystems because we can't be bothered to free our pointers...
To be clear: getting Rust programs past the compiler is much harder that C programs: C doesn't incorporate much type-system/static checking so a lot more gets past the compilers. But writing truly correct C programs (i.e. don't crash horribly) is a harder task, as there's no computer assistance for avoiding large classes of possible bugs.
> failing to free a pointer is rarely one of them
> we can't be bothered to free our pointers
Solving this "problem" is not the goal of Rust. Rust aims to avoid undefined behaviour by default, e.g. derefencing invalid pointers, use-after-free, iterator invalidation are statically ensured to never happen, unless you opt-in at specific locations via `unsafe`. Cleaning up memory automatically is a nice feature, but the main draw card and the reason Rust might actually be interesting is that Rust offers memory safety without garbage collection.
In any case, I think you're being overly glib about the problems of C: it's so so easy to have a program that exhibits undefined behaviour, and so is theoretically operating with no restrictions.
In short form, here, without a doubt.
> it's so so easy to have a program that exhibits undefined behaviour, and so is theoretically operating with no restrictions.
I've run into this. A lot! It was almost always my fault. One notable exception was back when I used to do a fair amount of with GP (Genetic Programming -- evolving large populations of small programs). The system was shockingly adept at finding/exploiting edge-cases and exploiting them for advantage.
Every C programmer already knows what kind of C issues exist, and how with C you can screw up things and what operations are unsafe.
It's not like he's saying something novel that will leave C programmers scratching their head as to the value of a safer language.
>There needed to be a discussion on the performance of Rust, and of its ability to call into C without overhead
He suggested they look at it, and he gave the basic 10,000 foot overview. It's not like C programmers (an industry with millions) will convert simply if some random blog post gave more specific examples...
Java was supposed to fix all of these problems, but it's a mess in its own way. There's no perfect language.
Almost simultaneously with C the Pascal language was introduced, completely safe and easier to learn. It enjoyed a period of popularity, and for a while it was widely taught in high schools and universities. Some big systems were built in Pascal, but every big Pascal app I've ever seen relied on external code in C (or assembly language) because Pascal is hobbled to enforce safety (see Brian Kernighan's article "Why Pascal Is Not My Favorite Programming Language, http://www.lysator.liu.se/c/bwk-on-pascal.html). Some of Pascal's faults were fixed in Modula and Oberon but those never got any traction.
To be absolutely safe the language is either crippled so it's missing important features, or it requires a runtime to sandbox everything, check pointer references, do garbage collection, etc. And that slows things down.
I don't see C going away any time soon in embedded software, which is where bugs and vulnerabilities can be most dangerous and where they are hardest to fix. It's not much of a disaster for a video game to crash, but it can be very bad if piece of hospital hardware or a flight control system has a latent bug.
There are tools that can analyze C code both statically and dynamically -- the language is not just old, it's mature.
I'm sure someone is already rewriting the Linux kernel and all of the Unix-y tools in Rust or Go or whatever. I won't hold my breath.
Not for the things C is good for. OS work, drivers, common libs like OpenSSL callable from other apps, infrastructure work, etc.
Besides Go is nothing new. There have been similar languages ever since 1970, and they are not replacing C.
[1]. https://www.codementor.io/rust/tutorial/steve-klabnik-rust-v...
The reason I ask is because I went to look at the docs for Rust, and they still seem incomplete.
As someone on the outside looking in... I'd have a hard time recommending Rust over C, because the learning path isn't complete yet. C is extremely well-documented. I want to learn Rust, because it's the new hotness, but it seems difficult to do right now.
That said, I'm very curious to hear your thoughts on this.
Also, if you have advice on how best to learn Rust, I'd love to hear what you have to say on that. My professional experience has been almost entirely with scripting languages, and nothing more low-level than that.
New programming languages are almost never designed with the new programmer in mind. I've spent a _lot_ of time teaching people to program, and they all have really serious hurdles. There are some good projects in this space, but they're a very, very tiny minority. Don't underestimate how hard "Hello world" is, which is actually something that that PDF I linked points out.
> they still seem incomplete.
Can you elaborate on this, with specifics? It'd be really helpful.
https://www.dropbox.com/s/vuax1ocki846llu/rust_example.mp4?d...
Downloading and installing Rust, no problem. Then I tried doing the "Hello, World!" example. Pretty sure I had it typed right in vim. It didn't work.
Then, I typed it in Sublime Text, it still didn't work. Then I thought, I forgot to set +x on the file, and I remember I hadn't set anything on the shebang line in the file, so I went on in the docs.
As you can see... I got to the "Learn Rust" page, and it wasn't ready yet.
First of all, I submitted a patch to remove that link to the old intro this morning, so that should go away.
Second, the issue was that you compiled the program, which worked just fine, but you never ran it! on http://doc.rust-lang.org/nightly/book/hello-world.html , the example shows:
$ rustc main.rs
$ ./main # or main.exe on Windows
Hello, world!
You did the rustc, but not the ./!As for "Learn Rust", that's one of two paths through the documentation, as the first page of the book mentions. I've been focused on the "Syntax and Semantics" section, which is the other path. "Learn Rust" is coming over the next two weeks.
Hopefully, the instruction was more than, "Wow, this guy is a bonehead." :D
I think some mindset changes are needed here, in that I'm used to dealing with interpreted languages, rather than compiled ones.
Yeah, the two-step process is different. In the next setup section, when Cargo is introduced, you end up with a 'cargo run' command that does the build and runs your code in one step.
I think Rust IS able to replace C, precisely for the same reason why Java and Go fail to do that: they have runtimes and they are garbage-collected. That means that you can't easily write a library in Java or Go and then call into that from another language without complicated interop and overhead. You can't write low-level code without headaches. Et cetera. Rust doesn't have a runtime, so it's like safe C++, for that matter.
Go has as much a runtime as C or C++ [0].
Reminds me of cultures. Differences between groups times themselves over the years and will mostly diverge from each others (not necessarily).
Each linguistic specificity will lead to different idioms, libraries and landscape; also fitting to different domains.
Some times, Lisps or algebra-based FP (in my inexperienced eyes it could lead to safier code too) makes me think there a way to reunite all this under the same roof though.
ps: I agree with your post. Maybe language matters to a certain extent only; good system design is language agnostic.
> I'm sure someone is already rewriting the Linux kernel and all of the Unix-y tools in Rust or Go or whatever. I won't hold my breath.
You're right, man. Nothing is perfect and we can't start with a blank slate so we might as well give up.
http://en.wikipedia.org/wiki/Nirvana_fallacy#Perfect_solutio...
I neither believe nor wrote that if we can't start with a blank slate we might as well give up. Since I specialize in working with legacy code that's actually pretty much the opposite of what I do.
What I meant by "I won't hold my breath" is that C has an enormous lead in maturity, programmers, and installed software base. Displacing it because a writer at TechCrunch thinks C sucks and Rust is better is a different kind of fallacy. I'd be happy to see a safer language that didn't sacrifice expressive power, performance, and memory footprint. Many, many candidates have been floated since C made its debut, so either they have all been found wanting, or the obstacles to moving beyond C are bigger than just inventing a safer language. That was my point.
There is a parallel here in assembly language. Once upon a time, programmers extended the viability of assembly language by building smarter assemblers with powerful macros and such. None of that is necessary if you minimize your use of assembly language, so that all you have in your system that is in assembly language is a few atomic instructions, some context saving/restoring code, interrupt dispatch, and a speedy memcpy and whatnot.
Going from C to Rust (or Go, or C++ or whatever) is kind of like trying to use a deluxe assembler to write large assembly language programs with better reliability.
If I'm going to leave C behind for some task, it's because I want to get away from that sort of semantics---any version of that semantics.
Even if you did decide to use Rust instead of C in C's typical domain - operating systems, drivers, and constrained resource devices - you would be forced into using "unsafe" blocks anytime you had to interact directly with the hardware, manually manage memory, or issue ASM commands; at which point you're as vulnerable to programming mistakes as C.
Yes, but if you start off in C's typical domain, then you cannot decompose your problem set into safe and unsafe bins using C. So you're stuck with unsafe-lowest-common-denominator everywhere you go.
It is not about completely removing unsafe stuff. It is about reducing the possible mistakes as much as possible.
Still, in theory isolating the possible causes is a fantastic advantage.
Things that worry me about Rust:
* Too much mandatory metaprogramming. Basic things such as I/O require generics, and even lambdas. Rust's cruft level in this area picks up where C++ with Boost left off.
* Error handling is too verbose. Wrapping things in "Result<>" and "Some<>", then unwrapping them with "match" is wordy. Wrapping that in generics makes it shorter but hides what's happening.
* The type system and type inference are very complex, and type errors induced from another module can be hard to diagnose.
* Too much language churn. Supposedly "stable" modules still keep changing. I'm surprised when I recompile a program after a few weeks and it still works. This needs to settle down very soon.
* Resource Allocation is Initialization. Initialization isn't that bad, but what do you do about an error discovered in a destructor? Destructors are always troublesome, and Rust doesn't seem to have that area under control. A "with" clause approach would have been cleaner. The recent discovery of a soundness problem in Rust destructors is being handled in a way that bears watching. First, the actual issue reporting the problem was closed without a fix.[1] The discussion was "moved to an RFC" so as not to block the release of Rust 1.0.[2] Related issues are now being discussed to death. A good discussion of the issue is at [3].
While Rust avoids complex run-time abstractions, it has very complex compile-time abstractions. Rust has all the cool abstraction ideas - closures, generics, templates, type inference, RAII, functional programming, etc. This may be too much for the C crowd. The basic libraries use all these features, so you can't restrict their use on a project.
[1] https://github.com/rust-lang/rust/issues/24456 [2] https://github.com/rust-lang/rfcs/pull/1066 [3] http://smallcultfollowing.com/babysteps/blog/2015/04/29/on-r...
However, at the same time I think the language researchers cannot help but add abstractions on today's languages. The unusual exception was Go, but it failed to target the same niche as C/C++ at the end. Actually I think there are more people who like abstractions than those who like the extreme simplicity of C. So unfortunately, a "direct" replacement of C will not happen in the foreseeable future, I think.
However, while the feature complexity can be avoided at least in the standard libraries, I think that's a more general (possibly cultural) problem, considering a Rust developer will have to use any libraries other than the standard libraries at the end.
I believe that's why the Go designers are so wary of adding new features to the language. If there's a feature, library writers will use that feature. Nothing can prevent this.
Problem is, sooner or later you need to be using the operating system for something more than just starting processes, be it controlling file handles or sandbox code or whatever. Then you need API compatibility and control over your mallocs. Which isn't impossible in non-systems PLs, but it isn't exactly a walk in the park either, as you're basically left calling wrappers for syscalls instead of doing it directly, which means you have master both your runtime and your C-based operating system.
At least this time they have an answer that's at least worth listening to. The newest crop of languages with Rust and Go actually make an effort towards system programming. That excites me! Now all you have to do is make something as secure and robust as Redis or Postfix, and let's see how it fares in production. And if Servo grows to be a fully grown competitive web browser, applying binary deltas to itself, sandboxing GL code, and everything in between, that would be a great litmus test for Rust to actually compete with C++.
> And if Servo grows to be a fully grown competitive web browser
From what I hear Servo is a research project. There exists a great chance it will never become a fully grown web browser.It already started sandboxing some code, but I'm not sure what binary deltas have to do with massively parallel web brwoser.
1, assembler
2, C + validated compiler + static verification + coding standard (MISRA-C maybe)
3, C++ + validated compiler + static verification + coding standard (MISRA-C++ maybe)
4, Java + specialized VM with RTSJ + static verification + coding standard (RTSJ)
5, Ada/SPARK + ???
So really, C/C++/Java/Ada aren't being used alone, it's the language + validated compiler + verification tools + a coding standard that are being used.How are Haskell, Rust, Go, and whatever other nouveau language of interest to the HN crowd tenable options for serious work when they lack the ecosystem of a formal standard, validated compilers and verification tools, track record, etc.?
Then I read this in TFA:
> Despite guaranteeing memory safety, Rust does not have a garbage collector or runtime, and one of the benefits of this is that Rust code can be called from C with no setup at all.
Maybe I should think about it as "you always enable ASan in your production binary"? That suits me just fine, if that's one of its major benefits. I will resolve today to follow up on Rust and try to learn more about it.
That said, defense in depth is always nice. kmc got AFL working with Rust recently, and it's uncovered some things. Using an ASan-like tool to do some of the runtime stuff would be great to have.
Those were problems with the rust compiler or the binaries it generates?
- https://github.com/rust-lang/rust/issues/24276
Sometimes you need to do things that could potentially cause problems (like every single pointer dereference) because there are deadlines for things to happen. If DMA had to check whether the place it's about to write the next 4 sample from the ADC is ok every single time, you might very well miss it
The main thing that will finally convince people that a high-level of abstraction is OK for OS level programming is for someone to get off their a$$ and build one. (That's actually usable. It would have to be SO usable that people could quickly build out the new user code to make something like *nix or windows seem like more of a hassle in contrast. That's such an insanely tall order...not sure how you get there.)
I think that the best you'll ever be able to do is to build systems with frameworks that are safe(r). People won't give up C because it makes them feel smarter. The pile of code for C based systems will be with us forever. A new system would have to be able to do all that AND be safe.
I cannot think of a single systems programming language that has not vowed to put an end to all this buffer overflow and dangling pointers mess. The ones I tried so far (I think there's a dozen...) failed because of at least of one of the following items:
* Some of them mainly achieve safety by forbidding any unsafe read or write -- and then when you have to do a read or a write, they implement that by cleverly eschewing a protection mechanism or by isolating everything in an unsafe area. The former is disastrous because you can have bugs that eschew the protection mechanisms, too (see Heartbleed -- which can be perfectly replicated in Rust or Go); the latter, while useful in most application software, is next to useless in my day to day activity, where I have entire modules that consist of nothing other than unsafe reads or writes. At that point I might as well be using C.
* Others mainly achieve safety through a complicated system of annotations ("this parameter must not be NULL") or a type system that makes certain guarantees ("these parameter can never be NULL"). The end result is a system of annotations and/or types which you can describe in about as much space as The C Programming Languages takes to describe the whole language. Barring the fact that my memory is limited after ingesting C++, real life experience shows that "more complexity" is how bugs get introduced, rather than resolved. Except that the new problems don't arise because a pointer is pointing where it shouldn't, but rather because 1 == 1 returns False for certain types of 1 or something.
* Aaand some of them simply delude themselves through clever sandboxing and protected writes, ignoring the fact that the sandboxing back-end is C all the way and can be exploited. Except it's so mind-boggingly complex that fixing it is incredibly complicated.
Historically, there have been many instances of operating systems written in high-level (ish) languages. Hell, there are operating systems written in Common Lisp. It can be done. People haven't flocked towards high-level languages despite 30 bloody years, if not more, of operating systems written in high-level languages because it turns out you usually can't do it unless you turn most of those fancy protection features off. At which point you end up with C, only with a more complicated syntax, and why the hell bother?
The key to writing secure software that doesn't crash is still in correct programming. Tooling can help, but no language, no matter how fancy and hand-holding, is going to compensate for that.
Pros:
- Pattern matching is beautiful, and is one of the things I miss most when not working in Racket.
- I love their static typing. I think they hit a sweet spot with type inference inside of functions but required types to be specified as parameters (too much inference can make it hard to know exactly what type something is, too little inference means I spend all my time writing type signatures).
- Borrowing and lifetimes are neat and have helped me avoid some lifetime-related bugs.
- Being able to call Rust code from C is a huge win, and I think this world needs more "systems-level" languages than C and C++.
Cons:
- Borrowing and lifetimes. This is actually is a huge pro, but sometimes the compiler can spot a lifetime issue that I don't understand (which makes fixing it hard). I'll get better with this with time, but associating lifetimes with object types is a bit confusing and hard to do if you want to do anything nontrivial. Again, I'm sure this is mostly because I'm a newbie, but it's also reality.
- Verbosity. Rust is actually concise for the most part, but if you want to pass a &str to a function, but have a String, (which, FYI, is fairly common in my experience, as most string-related functions take a &str), you've got to either call as_str() (which is unstable) or dereference-reference (i.e. &*) the String. There are a few simple things that require you to be more verbose than some other languages do.
Overall I like Rust. I think it's worth experimenting with, but I wouldn't start a really serious project with it right now (because it's so young and volatile). If someone wants to use it for a really serious project, I'd suggest checking out Rust in another year or two when it's a little more mature.
> if you want to pass a &str to a function, but have a String
You can just add a & to your String. &String coerces to &str. Deref coercions are one of the few places where Rust does an implicit coercion: http://doc.rust-lang.org/nightly/book/deref-coercions.html
Edit: Ah, here: https://github.com/rust-lang/rfcs/blob/master/text/0241-dere...
This left me only Rust, and it is a pleasure to learn. The compiler errors are very helpful, and so is the community. You can take your time and gradually discover new stuff, like the difference between heap and stack, static/dynamic dispatch, Move vs. Copy semantics, usefulness of tagged unions in combination with pattern matching and so on...
The author claims to have 'moderate' experience with Rust and C, at least.
It includes a story about how the Rust version actually caught an error in the C and Go implementation.
I fell in love with Rust. Usually I (internally) complain about a lot of things in languages. There are many drawbacks and bad design decisions. It might be hard to express but there's sense of engineering beaty. And I like Rust almost in every way.
So far I wrote a bunch of little programs and some simple parser (bitmessage protocol). I didn't have any real problems. Macroses looked a bit clumsy, I hope they'll see some overhaul, but they did their work. Much better than cpp anyway.
And I can say that it's just so easy to write code when you're 100% sure there are no problems with memory management. There are billion of other little things making life easier, but painless memory management is the big one.
I liked the approach of Learn C the Hard Way, (i.e. lesson 1: "Now compile the hello.c with -Wall", lesson 2: "Lets learn to write simple makefiles",..., lesson 4: "Lets use Valgrind", ...)
But the latest revision of the last chapter that I remember had quite a good critique of K&R's C writing style, now contains just a rant on internet trolls (?). http://c.learncodethehardway.org/book/krcritique.html
Perhaps there should be this unwritten rule, that if a post has made it to the front page of HN, it means enough HN users voted it, and it's interesting to them, so it's counter-productive to dismiss it as uninteresting...
The counterexample to the "C is unsafe" argument is that C is what people use for extremely high-reliability programming. I prefer Haskell, and Rust is promising, but the fact is that JPL uses C. The Mars Rover is probably running C code. While C is insecure as hell under typical corporate use (tight deadlines, unskilled programmers) it can be pretty damn reliable in the right hands.
Boeing does...
http://www.engadget.com/2015/05/01/boeing-787-dreamliner-sof...
Ultimately if a newer, tighter, safer C is going to arise it must come fron the GCC or LLVM people.
Go: owned by google so it's a no-go.
It's actually an open source project, and many of the core contributors don't work for Google. Just thought I'd point this out as it seems to be a common misconception.
Given that the CIA recently bragged that they were able to put spyware in iOS apps because they have somehow exploited Xcode on developers' machines, one has to think about the implications of downloading executables from companies that are complicit in spying.
Just because I do something safe e.g. I compiled the compiler myself, it is the larger implication of everyone else not doing it right that is the concern.