8 ms·
> I know there's a sentiment here on HN against C I think there's two major against-C groups: those of us who have worked with C for decades and those who neve
by wyldfire 10y ago
> I know there's a sentiment here on HN against C
I think there's two major against-C groups: those of us who have worked with C for decades and those who never worked with it. I'll try and speak for those of us who've used it for decades. The popular high-level languages that have arrived since ~1995 (Java, Python, JS, C# and friends) are excellent productivity increases. In general, they sacrifice memory and performance in favor of robustness and security. For enormous software problem domains, we just don't need C's complexity or error-proneness.
Until Rust, there's been very close to zero serious competitors for C if I wanted to write a bootloader, OS, or ISR. Not even C++ could do those (without being extremely creative on how it's built/used). The ~post-2000 languages (golang, swift, D etc) can't do that (perhaps D's an exception but it wasn't an initial goal AFAICT). This is huge, IMO.
We've groaned and grumbled about how hard it is to parse C/C++ code for decades. This is a big deal for tooling. Because of the language's design, even if you use something "simple" like libclang to parse your code, you still have to reproduce the entire build context just to sanely make an AST. All of those other new languages above probably address this problem but also add all kinds of other stuff which we can't have for specialized problem domains (realtime/low-latency requirements, OSs, etc).
> collection of ... software not going to be rewritten in eg. Rust or some (lets face it) esoteric/niche FP language
IMO it's not appropriate to lump Rust in with "nice FP language"s. And don't look now but lots of stuff is being rewritten in Rust. Fundamental this-is-the-OS-at-its-root stuff: coreutils [1], "libc" [2], kernels [3], browser engines [4].
[1] https://github.com/uutils/coreutils https://github.com/uutils/coreutils
[2] https://github.com/japaric/steed https://github.com/japaric/steed
[3] https://github.com/redox-os https://github.com/redox-os
[4] https://github.com/servo/servo https://github.com/servo/servo
- tannhaeuser 10y ago> IMO it's not appropriate to lump Rust in with "nice FP language"s. Maybe I should have expressed it better, but I didn't intend to lump these together. >And don't look now but lots of stuff is being rewritten in Rust. I'm myself cautiously optimistic re Rust, but having been burnt by C++ in the past I'm not enthusiastic about fighting language idiosyncrasies (though modern C++ certainly deserves a second look). Then there's the issue (some might argue it's a plus) that Rust is at the same time a language, a lib, and the only compiler implementation (unlike C or C++ which give you choice).
- cat199 10y ago> Then there's the issue (some might argue it's a plus) that Rust is at the same time a language, a lib, and the only compiler implementation This was essentially the case for C and C++ as well for somewhere around 5-15 years a piece, depending on how you measure it.
- smitherfield 10y agoParsing is IMO of all the complaints you could make about C/++ rather bikesheddy. Parsing is a solved problem. Modern compilers can parse millions of LoC per second. And most of the specific parsing-related complaints (pointer dereference or multiplication?) about C/++ are also true of Rust. (Edit: Nope, brain fart on my part, see below). And, AFAIK, all C/++ parsing is well-defined, if counterintuitive in certain edge cases.
- steveklabnik 10y ago> most of the specific parsing-related complaints (pointer dereference or multiplication?) about C/++ are also true of Rust. This should not be true, and we fought hard to keep it that way. There's one spot of Rust's grammar that's context-sensitive, for something very rarely used, and other than that, it's all much simpler.
- smitherfield 10y agoYou're right. AFAIK types and identifiers are always unambiguous in Rust. I was thinking visually (same operator) instead of in terms of specification and implementation. Shows me to make flippant comments from the toilet! My larger point is that there are plenty of very good reasons to criticize C/++, and parsing is a minor one since parsing is fast, and even if the creation of the AST isn't context-sensitive, verifying its correctness (is this identifier in scope?) still is. What is the context-sensitive spot in Rust's grammar?
- steveklabnik 10y agoFair enough :) https://news.ycombinator.com/item?id=13915595 https://news.ycombinator.com/item?id=13915595
- wyldfire 10y ago> plenty of very good reasons to criticize C/++, and parsing is a minor one Ok, fair bit, it's a frustration for me but admittedly not as important as the other differences. I mention it because it's a wart in C's language design and I figured Rust's safety features are already well-known and heavily discussed. If I want to write a simple tool "ask this tree of .c files how often they use an identifier with name 'X' or type 'Y'", I have to find out the include paths, defines, all kinds of other "noise" just to find out what could be a relatively simple query of the source base.
- mundanevoice 10y agoI hope Rust doesn't face the same fate as other ambitious projects by Mozilla. Rust has a quite unusual syntax compare to any other systems programming language. Also, there is a big learning curve. Keeping all the benefits aside, I really hoped Rust had a simpler syntax. I really think, one day a language will borrow the good parts of Rust with a simpler syntax and get ahead of it. Rust in its current form will never be as successful as C/C++.
- naasking 10y ago> Rust has a quite unusual syntax compare to any other systems programming language. In what way? It's very C-like in its basic syntactic structure, just more regular.
- mundanevoice 10y agoWell, I was not talking about basic syntactic structure. For eg: take a look at this https://github.com/servo/servo/blob/master/components/constellation/event_loop.rs#L18 https://github.com/servo/servo/blob/master/components/conste... I am not against Rust. Rust has some great ideas and intent. I just feel they should have created simpler syntax. A more complex and unusual syntax doesn't have any real benefits IMO.
- naasking 10y agoYou still haven't pointed out what syntax is problematic exactly. I've never programmed in Rust, but I don't have any trouble reading it coming from a C and C# background. What exactly is the problem with the example you linked to?
- pcwalton 10y agoWhat's the issue with that example?
- naasking 10y agoNot having programmed in Rust, the fact that Rust requires all type parameters to be used, thus ruling out proper phantom types, was semantically surprising to me. But I don't understand what syntactic issue the other poster had with that example.
- zeveb 10y ago> Until Rust, there's been very close to zero serious competitors for C if I wanted to write a bootloader, OS, or ISR … We've groaned and grumbled about how hard it is to parse C/C++ code for decades. I honestly think that Common Lisp can do this quite well. It was designed to be a high-level language, but it's completely capable of working at the machine level, pleasantly and easily. Unlike C, most of the time one has safety, but one can disable safety when necessary with a simple (declare (safety 0))). Performance is extremely good with modern compilers, although I don't know how good they would have been back in the old days. And it makes parsing the language dead simple. Here's the entire reader algorithm: http://clhs.lisp.se/Body/02_b.htm http://clhs.lisp.se/Body/02_b.htm From what I can tell of Rust, it doesn't look easier to parse than C (but I've not looked deeply); certainly, it's orders of magnitude more difficult to parse than Lisp. I believe that Standard ML or OCaml could do similar things as well, albeit at the cost of being more difficult to parse. Smalltalk is maybe a little less capable, but somewhat easier to parse. They've all been around for decades.
- wyldfire 10y ago> I honestly think that Common Lisp can do this quite well I haven't used any lisp dialects for decades, so I have naive questions: is there really sufficient support from compilers+linkers to write a bootloader in lisp? Do I have to do a lot of bootstrapping in assembly to bring up lisp interpreter before I can execute the lisp code or does the ahead-of-time-build result in executable machine code? Can I do inline assembly (not required but a really key benefit IMO)? Are there numerous examples where someone's already written one in lisp?
- sanxiyn 10y agoFor an example of operating system kernel completely written in Lisp, see https://github.com/froggey/Mezzano https://github.com/froggey/Mezzano
- tjalfi 10y agohttps://github.com/dym/movitz https://github.com/dym/movitz is a Common Lisp system that runs on bare metal x86. The source code is quite readable. The rest of this post is an excerpt from an email I sent 6 years ago. The following comments on runtime systems are partially based on a long c.l.l thread with posts by Lucid, Symbolics, and Franz alumni. Franz uses a 3-layer approach: CL, a low-level Lisp, and C. Lucid started with Lisp that generated assembler but reluctantly added some C. Symbolics Lisp Machines used bootstrap code in a Pascal-level language with prefix syntax. A Symbolics alum said that in retrospect they should have used C. Most Lisp implementations have subprimitives - low-level functions that can circumvent the type system, often with a prefix such as % or :. Assembly language integration dates to Lisp 1.5 and there are several common approaches. 1. turn the optimizer off - this is easy to use and implement. 2. optimize the assembler block - Naughty Dog GOAL did this. 3. annotate the assembler block with pragmas that indicate side effects. This can be error prone and difficult to use. https://www.pvk.ca/Blog/2014/08/16/how-to-define-new-intrinsics-in-sbcl/ https://www.pvk.ca/Blog/2014/08/16/how-to-define-new-intrins... is an example of this approach. Edited to add the SBCL intrinsic link.
- madphrodite 10y agoNo competent C programmer is going to switch to rust based on the projects you've just mentioned. The comments in the commits speak for themselves.
- wyldfire 10y agoPlease, connect the dots for me. An initial skim of the commit messages did not yield any egregious "Utter Disregard for Git Commit History" [1]. Even if it did, it may just mean that the maintainer is focused more on results and robustness than preserving a pristine history of the project. [1] https://zachholman.com/posts/git-commit-history/ https://zachholman.com/posts/git-commit-history/
- madphrodite 10y agoDon't think that was where I was going. It was more of the none of this stuff is really done, it is hard in this language and help would be appreciated..as understood through the project splash page and then examined through commits?
- Manishearth 10y agoCould you expand on why you think Rust is a serious C competitor for OS/ISR/bootloaders but not C++? This statement intrigued me. I thought C++ had naked functions and all the things you need to write an OS.
- wyldfire 10y agoI might have to walk that back. It seemed to me that no_std was "more straightforward" and/or "more formalized" than "#pragma interrupt" (etc). But I could be wrong there -- if so, mea culpa (the post is no longer editable).
- Rusky 10y agoRustc did recently get a "x86-interrupt" calling convention, but that's unrelated to #[no_std], and only works on x86. Either way, "#pragma interrupt" should work just as well in C++ as in C, since C++ doesn't really change any aspect of the language that matters there. Further, even in C I rarely see use of "#pragma interrupt"-like tools- rather, everyone still seems just to use per-platform assembly glue code. (To be fair, my experience is mostly in kernel code for things like Linux, rather than standalone embedded applications where "#pragma interrupt" would be more valuable.)
- Manishearth 10y agono_std is more formalized, though C++ enforces the same thing by failing to link if you try using malloc (or whatever) when writing a kernel. no_std also means that it's very easy to tell if a crate works without the stdlib, so you can use code from the ecosystem instead of rolling your own. Ultimately the Rust OSes resort to some handwritten assembly as well. I think that's going to be a constant of writing a kernel. Rust is working to minimize it (e.g. with things like `extern "x86-interrupt" fn`), but at a kernel level there are just some kernel specific asm instructions (like all of the TLB stuff) that either compiler will probably never support generating without inline asm. So while Rust may be better than C++ at writing OSes (I'm not sure! I haven't looked at all the stuff you need to write an OS in C++), I do think they're in the same ballpark, close enough that if Rust is a "serious" competitor C++ probably is too :)
- nwmcsween 10y agoThe rust coreutils is an excellent example of the issues of having such a mess of abstractions, the resulting binaries are literally magnitude larger than busybox equivalents.
- wyldfire 10y ago> issues of having such a mess of abstractions, the resulting binaries are literally magnitude larger They're significantly larger, yes -- it's a fair complaint of rust. But it's mostly because of static linkage AFAIK [1] and not "a mess of abstractions". [1] https://github.com/uutils/coreutils/issues/747 https://github.com/uutils/coreutils/issues/747
- nwmcsween 10y agoAnd what do you think pulls in all the symbols for static linking? Maybe the mess of abstractions perhaps?
- ssokolow 10y agoActually, the culprit is Rust's decision to statically link its standard library and all its dependencies by default. Things like libunwind, libbacktrace, embedded debugging symbols for backtraces, and the jemalloc allocator aren't free. If you ask for dynamic linkage (with the caveat that Rust doesn't have a stable ABI yet), you get a ~8K Hello World binary. It's also possible to prune down the statically-linked size by opting out of various conveniences like jemalloc. (They're working toward making the system allocator default but don't want to regress Servo in the interim.) ...and if opt into static linking with GCC and G++ (and ask Rust to make its link to libc static), Rust can actually outdo them on a Hello World. Here's a detailed exploration: https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html https://lifthrasiir.github.io/rustlog/why-is-a-rust-executab...
- nwmcsween 10y ago> Actually, the culprit is Rust's decision to statically link its standard library and all its dependencies by default. No it really isn't, static linking does not imply bloat as commonly perpetuated. > It's also possible to prune down the statically-linked size by opting out of various conveniences like jemalloc Try this: opt out of everything except the standard library, create something somewhat trivial and idiomatic in both rust and c, compile and see what you get. > Rust can actually outdo them on a Hello World. Hello word is hardly a use of the standard library.