
Why you should, actually, rewrite some of it in Rust - JoshTriplett
https://unhandledexpression.com/2017/07/10/why-you-should-actually-rewrite-it-in-rust/
======
ekidd
The author focuses on rewriting low-level media decoders in Rust. I have bit
of experience in this area. I've been slowly working on an MPEG2 binary
subtitle decoder in Rust: [https://github.com/emk/subtitles-
rs](https://github.com/emk/subtitles-rs)

A while back, I ran my subtitle decoder through "cargo fuzz", and I was
pleasantly surprised at the results: Close to half a billion fuzz runs found 5
runtime panics, all of which were detected by Rust before they could
compromise security. If I'd written this code in C, several of those errors
would have been exploitable. I like to think I'm a lot more paranoid than the
average programmer. But the MPEG2 format is gnarly and, sooner or later, I'll
miss a potential overflow when bit shifting, or get confused following
internal "pointers" in a subtitle packet.

Rust has a few advantages for this work:

1\. Rust does not require a garbage collector or other specialized runtime.
This makes it _far_ easier to pretend to be boring C code. This is a
significant advantage over some other excellent languages like Haskell, etc.,
which require non-trivial runtimes.

2\. Rust is very fast by default. For low-level programming, this matters.

3\. The Rust infrastructure for testing and fuzzing is surprisingly good and
easy to use, which makes it easier to produce bullet-proof libraries.

The downside is that even if you're already familiar with C++, it's probably
going to take a couple of weeks to become comfortable with Rust. And if you
don't really understand stacks, heaps, memory layout and references, it may
take even longer.

I do agree with the underlying thesis: In 15 years, I'll be heartbroken if
we're still facing an endless stream of security updates and remote root
compromises. But it's going to require literally billions of dollars of
programmer time to put a dent in this problem.

~~~
yjftsjthsd-h
> Close to half a billion fuzz runs found 5 runtime panics, all of which were
> detected by Rust before they could compromise security. If I'd written this
> code in C, several of those errors would have been exploitable.

Wouldn't the apples-apples comparison be fuzzing a C program? If AFL would
catch the same bugs, Rust isn't better than C in this case.

~~~
ekidd
> If AFL would catch the same bugs, Rust isn't better than C in this case.

You can find a list of my vobsub bugs in the Rust Fuzz Trophy Case:
[https://github.com/rust-fuzz/trophy-case](https://github.com/rust-
fuzz/trophy-case) They are:

1\. The shift overflow: This was harmless in both C and Rust, I believe.

2\. The arithmetic overflow: This was a runtime panic in Rust, resulting in a
clean crash. In C, this might have been exploitable with a complex enough
attack.

3\. The three "invalid slice" errors. All of these were clean runtime panics
in Rust, and I'm pretty sure that at least two of them would have been
exploitable in C with enough work.

So Rust has a couple of advantages here. Out of 5 errors found by the fuzzer,
the worst thing that could happen in Rust would have been crashing the program
in a controlled fashion. In C, we would have been looking at memory corruption
and quite possibly escalation.

Secondly, Rust's runtime checks actually make fuzzers work better. Even simple
checks like "is the end index of this slice great >= to the beginning index?"
tend to catch problems almost as soon as they occur.

~~~
jcelerier
You would get 2. and 3. as runtime panic in C if you used sanitizers.

~~~
the_why_of_y
You would also get additional attack surface if you used sanitizers, because
they were designed as debugging tools, not for production deployment.

[http://seclists.org/oss-sec/2016/q1/363](http://seclists.org/oss-
sec/2016/q1/363)

------
falcolas
Sigh. This again.

Not all security vulnerabilities are due to pointer arithmetic or out of
bounds execution or pick-your-rust-is-better-idiom.

Heartbleed is a great example. It wasn't caused by an error with how C handles
memory or strings or anything else. It was caused by failing to validate
untrusted input. Rust isn't going to help you with that.

Could the affected parts be re-written in a way where the boundary checks
would have worked? Yes. As they could have in C, C++, D, Fortran, or any
number of other languages.

If memory safety and correctness was so truly valued above everything else,
we'd be seeing a lot more Ada than we do today.

And this doesn't even cover the fact that not everything _can_ be re-written
in Rust, mainly due to compilation target restrictions.

Exasperated Edit: No, I'm not saying code should never be re-written. I'm not
even saying that Rust is a bad tool if the decision to re-write is made. What
I am saying is that Rust is not a silver bullet. Rust is not going to solve
every problem ever. Rust is one of many choices for writing memory safe, C ABI
compatible code.

The reasons provided by this article are insufficient to solely justify a re-
write in Rust; the exact same arguments could be used as justification for
rewriting everything in Ada. Or Haskell. Perhaps some thought will be put in
the next "re-write everything in Rust" article that takes that into account.

Then again, none of the past articles have.

~~~
fpgaminer
Sigh. This again.

The Rust language is more than just memory safety. Memory safety is certainly
the most prominent and unique feature of Rust, but the language also includes
a lot of features that make writing good, bug-free code easier.

For example:

`Result`, when returned, forces all callees to check for errors. It's all too
easy to throw away error codes in C/C++. This is probably one of the most
important features in Rust, besides memory safety.

To easily consume an `Enum`, you have to use `match`, which forces you to
handle all members of the enum. In C/C++ it's easy to build a switch for an
enum, and then later when a new member is added to the enum and you forget to
update all your switches.

Support for multiple return values (through tuples) means being able to avoid
the nightmare of output pointer arguments to functions.

No NULL pointers.

Unit testing built into the canonical tooling.

Documentation testing built into the canonical tooling.

The `Option` type, which lets you return "nothing". In C/C++ you'd have to
return a bool, and again use a nightmarish output pointer argument.

`OsRng` in the rand crate (go ahead, try to write a function that safely reads
urandom in C).

No undefined behavior.

The list goes on. The flavor of it, is that Rust makes it easier to write
defensive code. Defensive code is code that is hard to use wrong. For example,
for a backup solution I built using Rust, I made all the cryptographic types
their own unique, opaque types. EncryptionKey, HmacKey, Salt, etc. For someone
new to the codebase, these types make it obvious what each function is asking
for, and make it really difficult to use the functions or the types the wrong
way. And they are super easy to build and use in Rust, see my code:
[https://github.com/fpgaminer/preserve/blob/master/src/keysto...](https://github.com/fpgaminer/preserve/blob/master/src/keystore.rs#L20)

C++ would require a whole set of classes, and require instantiating a new
class every time I want to add a new cryptographic type. Rust only requires 3
lines per type.

TL;DR: The real magic is that it's easier to do the right thing in Rust.

~~~
sidlls
I mainly agree that Rust is more than about safety. I just wish these other
features had a tenth of the vocal support as Safety.

It's even worse here on HN, where the a certain group seem to thrive on
blasting not-Rust, especially if that not-Rust is C or C++. I've seen comments
to the effect that anyone writing in C or C++ today is literally acting with
reckless indifference to life and safety. It's terrible.

~~~
fpgaminer
Criticism is just so much easier than constructive arguments. That's why it's
rampant in communities, even HN. And controversial subjects (Bitcoin, health,
food, Javascript) will magnify the problem.

It's easy to find fault in C/C++. It's easy to parrot the Rust marketing. It's
not so easy to actually have spent the time to learn and use Rust, let alone
articulate the subtle ways that its design, taken as a whole, is better than
alternatives.

Building those constructive arguments is _hard_. I remember reading an article
about error handling. It had to have been at least 10 pages worth of content,
covering the history of error handling throughout all the old and modern
languages, and using examples for where each fails or succeeds. It culminates,
more or less, in explaining exactly why the error handling of languages like
Rust outperforms in the field of systems programming.

(NOTE: The author of that article was actually arguing for the error system
used in the language they developed for their OS, IIRC. But Rust was pretty
darn close to the ideal the author was going for, and they pointed out as
much.)

My point is that it took 10+ pages to make a solid argument for _just_ error
handling. Not many people have the experience, wisdom, time, and willingness
to write even a fraction of such insightful content in a comment on HN.

That said, well written, balanced comments still generally float to the top in
even controversial HN threads.

EDIT: I would also like to point out something specifically in response to
"I've seen comments to the effect that anyone writing in C or C++ today is
literally acting with reckless indifference to life and safety."

There is a tremendous amount of pent up anger in the programming community
against C/C++. The language should have been put our to pasture a _long_ time
ago. Rust is the first language which has a real chance to displace it. So
it's no wonder that a lot of us are willing to just burn C/C++ to the ground
and jump ship.

Not to mention that as programmers we may be quickly approaching a time when
security bugs become a governmental issue, and the _last_ thing we want is the
government mandating how we write code. So it is in everyone's interest if we
self regulate. Again, that would explain why everyone is kinda jumpy about C
and security bugs.

~~~
pjmlp
The interesting bit is that it was already happening on the PC world, with C
only being used for OS APIs on Windows and OS/2, but most people were actually
using C++ (OWL, VCL, MFC), Turbo Pascal for Windows followed by Delphi, VB,
Eiffel, Ada, Oberon, Component Pascal...

Then the UNIX FOSS, written mostly in C took over, and set us almost 20 years
back in security and safety.

------
vvanders
+1 to everything said here.

It really is very straightforward to start inserting Rust into a C/C++
codebase. I did the Rust port of netcode.io[1] which started out by just
calling C code for the portions that I didn't have working in Rust. Eventually
the port was 100% Rust but I was really impressed with how seamless mixing
C/Rust was.

Even now our unit tests run against the C code at a functional level so we
know when the protocol diverges which lets us easily catch breaking changes
from the reference C implementation.

Much like everything, don't apply an adage blindly and do a proper evaluation
but I think if you're in the a space where memory or performance matters Rust
is a very strong contender that plays well with existing ecosystems.

[1] -
[https://github.com/networkprotocol/netcode.io](https://github.com/networkprotocol/netcode.io)

------
_dcwr
As someone who really likes C and C++ I feel like the biggest reason for me
not even thinking of Rust as a viable language right now is the community
attitude. It seems so damn hostile to C and C++. Anyone who thinks C and C++
is viable is a misguided idiot, bad programmer, writing unsafe software, etc.
to these people.

Some badass below even just said that "writing C++ code longer than a few
lines without UB is humanly impossible". Apparently all these FUBAR C/C++
systems (Linux and Windows kernels, all? web browsers, LLVM that the dear rust
uses, device drivers, web servers, router software, VMs of Lua, Python, C#,
Java, ..., etc.) work well enough to transfer that kind of garbage to me.

It's a bit like the decades old vim vs. emacs, C vs. C++, C++ vs. C# vs. Java,
etc. feuds.

~~~
moomin
Show me the programming language community that's a fan of the language it's
intending to supplant and we'll talk. :)

~~~
FRex
I've never seen Python or Lua community do that so far. But Python is its own
animal (which IMO is only highlighting how good it is), it's not supplanting
anything. Rust meanwhile both hates C and piggybacks on it (using LLVM,
similar syntax, cited compatibility and coexistence with C). Lua is similar,
no hate, its own animal, custom syntax, good in its niche (Python's niche
seems to be literally everything not super performance critical BTW). Same for
Erlang and Elixir, custom, different languages that are masters of their niche
(and go see people gushing over these two on HN, they don't shit on other
languages but say how good these are, no "Pfsshh, it's 2017, only idiot would
write a reliable service in C, you can't program C ever, it's broken").

~~~
tspiteri
_I 've never seen Python or Lua community do that so far._

Back then, wasn't it readable Python versus unreadable Perl?

~~~
moomin
You have a point. Python was definitely pitched as a perl replacement. We
forget because it pretty much won.

Lua has it's own problems, of course, but I don't see it as a language
developed as a consequence of frustrations with another.

~~~
kibwen
Lua was developed for legal reasons: huge tariffs on foreign software meant
that Brazilian companies often found it cheaper to develop custom toolchains,
which is what gave us Lua.

------
pornel
I've been programming in C for 17 years. Now I'm writing (and rewriting) image
processing and encoders in Rust and I love it.

It's not just memory safety. Rust has sweet error handling — the syntax is
almost as noise-free as exceptions, but code flow is as predictable as error
codes. In Rust I handle rare error cases that I wouldn't have bothered with in
C (and thus my Rust programs fail in orderly fashion instead of running with
garbage values).

Runtime arithmetic overflow checks have saved me many times. Knowing I have
this safety net I can be cavalier about using smaller integer types.

I don't have to free memory explicitly, and yet peak mem usage is as minimal
as it can be, and I did not have a leak yet.

And the whole ecosystem is fantastic compared to C. There's a culture of
having unit tests. Ease of using dependencies via Cargo, and the zero-cost
abstractions, help having dependencies broken down into small and focused
libraries.

~~~
CalChris
> the syntax is almost as noise-free as exceptions

No.

 _const USAGE: &'static str = "usage string";_

I _did_ rewrite it in Rust. I rewrote a small low level command line app in
Rust. I really wanted to like Rust going in but I freaking hated it going out.
They had a few good ideas but they never knew how to say no. The part I hated
the most was the Rust macro language. And for real low level to the metal
systems programming, everything is unsafe; so you may as well be writing in C.

Go is pretty minimal. They knew how to say no. I like modern C (C99 and C11).
It has most of the good ideas from C++.

~~~
steveklabnik
As of the most recent release, that's now

    
    
        const USAGE: &str = "usage string";
    

and there's talk of maybe making it

    
    
        const USAGE = "usage string";
    

> The part I hated the most was the Rust macro language.

We don't like it either; there's a replacement coming. We reserved the "macro"
keyword before 1.0 and made the current one use the less-good "macro_rules" to
help ease the eventual transition.

~~~
GrayShade
> We don't like it either

But.. but.. Scheme macros are so nice!

~~~
steveklabnik
Totally! It's not about the fundamentals, it's about things like "macros are
global" and "almost entirely hygienic but sometimes not", stuff like that.
Stuff that would be backwards incompatible to just fix.

------
jeffdavis
I am a fan of rust, but this article overstates the simplicity of C/rust
integration.

A few examples:

* You may need to use PhantomData in your struct definitions to properly track lifetimes. Not sure I fully understand.

* C functions don't always return. Sometimes they longjmp (c.f. PostgreSQL). Calls into C code need to prepare for this and it's not trivial nor does it generalize to all projects.

* Integrating with the memory management scheme is probably possible but not trivial. For instance, Postgres memory contexts (regions/arenas) can hold any kind of object, and code can switch between them and create/destroy them at will. You could rely on rust to manage it's own memory, with circular references an Rc that might leak.

* Postgres has its own calling convention for SQL-callable functions and conventions for loadable modules. Integrating with this is a pain because macros are hygenic and can't make up new symbols. Maybe procedural macros will help when they arrive.

* Rich APIs that rust supports don't always translate well to C. When integrating with a large application that pushes you toward the lowest-common-denominator APIs.

* A lot of C macros don't translate well to rust and just including a C header in rust is a lot of work. Hoping procedural macros will help here too.

~~~
anarazel
Btw, are you planning to work on a thin integration layer between rust and
postgresql? Sufficient to be able to write UDFs in rust? That's imo the most
realistic start to get some experience w/ rust in the postgres community, and
it provides value on its own.

~~~
jeffdavis
I've made some progress. Not quite as thin as I had hoped ;-)

I really need procedural macros to stabilize, otherwise it just puts too much
pain on the UDF author.

~~~
anarazel
> I really need procedural macros to stabilize, otherwise it just puts too
> much pain on the UDF author.

Why exactly is that? I'd have expected a C wrapper to take care of most of
that? Are you concerned about the Datum<->native type C macros?

Are you planning/hoping for this to be all safe, or is there going to be
unsafety in the wrapper?

~~~
jeffdavis
Creating a new module or UDF requires exporting new special symbols for V1
calling convention.

Rust's hygenic macros don't let you do that. So defining a UDF has a bunch of
boilerplate, unless I am missing something.

Procedural macros should alleviate this, but it will be a while. I might just
release and document the boilerplate needed.

EDIT: Yes, I am trying to be mostly safe. I will see how close I can get for
at least the basic stuff.

~~~
anarazel
> Creating a new module or UDF requires exporting new special symbols for V1
> calling convention.

Right - but I don't think you need to do so. That'd only be required if you'd
use language 'C' \- why not instead use language 'rust'. That'll force a
simple wrapper function which looks up and calls the real function, but that's
fairly harmless, no? Might actually be good, because you can do some extra
checks and conversions there.

~~~
jeffdavis
There are some extension APIs that demand C. But perhaps I dismissed the idea
of plrust too quickly and there is room for both?

~~~
anarazel
I'd say it'd be better to let PL handlers decide whether they can be used in
those cases. IIRC those cases largely exist because you need to be able to
deal with pointers which existing pls weren't able to...

------
ZoFreX
Is it really the time, yet, to start advocating for rewrites in Rust? Worth
considering for new projects, definitely. Using to write new components of
existing projects, maybe. But rewriting existing work?

I'm concerned if we start pushing that too much too early, the initial
friction will burn people. You only get one first impression.

For anyone who thinks "yes, now is the time" I have questions for you:

Have you released and distributed software written in Rust? What's the install
story like for end-users? Is it in a repository for a major OS (e.g. can I yum
install it, or apt-get it) and if so which?

As far as I can tell there's no major package manager accepting or even ready
to accept programs written in Rust yet. Am I misinformed?

~~~
steveklabnik
> As far as I can tell there's no major package manager accepting or even
> ready to accept programs written in Rust yet. Am I misinformed?

At least Debian, Fedora, Arch, and Gentoo have rustc and cargo in their
repositories. Oh and Alpine, recently. FreeBSD (IIRC) has it in ports.

In the not-too-distant future, there's a pretty important package that will
require Rust: Firefox. HEAD already does today.

~~~
cyphar
But you do realise that pressuring distributions in that way (when there are
still issues with integrating cargo and crates into our existing build
systems) won't result in Rust being properly supported right? There will be
some hack to get Rust to work "well enough" to build Firefox, and if someone
wants to start packaging more Rust stuff then all of the work would either
need to be redone or started from scratch.

I'm saying this as someone who has started writing their projects in Rust and
has plans to ship said projects as part of openSUSE and eventually SLES. If
you cannot satisfactorily maintain a Rust package inside OBS then it's a non-
starter. I imagine that many other distributions feel this way. We were burned
with Ruby (and continue to be burned by it) and I doubt that anyone is going
to jump on the "just auto-generate an RPM for every version of every crate we
need" solution.

It's great that Firefox will have Rust code, but I'm not convinced that will
make the situation better for distributions. It'll just make the packagers
more stressed out.

~~~
steveklabnik
All I can really say is, we're interested, willing, and actively working with
distro maintainers on fixing their issues. It's something we care about.

It is just one of many things on our plate, though.

------
Animats
The trouble with mixing Rust and C/C++ is that you tend to get Rust with C/C++
unsafe pointers. Most of the examples of this pass C raw pointers into rust,
Rather than passing a Rust Vec into C. Or even passing STL strings and vectors
into Rust. So, instead of passing Rust's array size information into C, they
propagate C's lack of size information into Rust. Typical example: [1]

    
    
        pub extern "C" fn count_substrings(value: *const c_char, substr: *const c_char) -> i32 
    

If you incrementally convert from C/C++ to Rust, you end up with those
constructs in the final Rust program. Not good.

[1] [http://siciarz.net/24-days-of-rust-calling-rust-from-
other-l...](http://siciarz.net/24-days-of-rust-calling-rust-from-other-
languages/)

~~~
dbaupp
You only end up with them in the final program with a naive translation that
makes life hard for yourself. Using the native types is much nicer (even
ignoring any safety benefits), and so people are naturally encouraged to do
so. For instance,

\- the C interface can easily be thin wrappers around pure "nice" Rust
functions, that just do the necessary unsafe conversions

\- and, whether or not that is used, when a function is no longer being called
from C (that is, everything that calls it has been converted to Rust) the
arguments can be switched to something more natural and strong static typing
will point out all the places that need to be updated.

I cannot believe that a pure Rust codebase would want to be passing raw
pointers around. And the alternatives don't seem at all better: only ground-up
complete-rewrite migrations (hard to justify so often won't even start) or not
migrating at all (meaning one has a C codebase that is still full of unsafe
pointers).

------
apenwarr
The title ought to have been, "Why you should rewrite _some_ of it in Rust."
That's what the article is actually about. Then we could have a productive
discussion about which parts, rather than yes or no.

~~~
dang
Good idea. We've added that bit above.

------
dman
Am waiting for Coq developers to start showing up and asking for Rust projects
to be rewritten using Coq / Compcert.

~~~
Jweb_Guru
Compcert is guaranteed to compile _correct_ C code to correct assembly (given
its TCB assumptions). It doesn't say anything about incorrect C code. So in
order to benefit from CompCert, first you have to prove that you have valid C
in the first place, which is no easy feat. You'd probably be better off using
a language that's designed with verification in mind, like Cogent or
ADA/Spark, or waiting for lambda-Rust to become production ready, since in
those cases you won't have to prove tedious bureaucratic memory safety
invariants for the vast majority of your code, and can focus on functional
correctness (or, if you just care about memory safety, you can stop there).

------
youdontknowtho
"Rewrite it in Rust" is the new "Stallman was right" thread.

Its an echo chamber of fandom. Over time I've come to despise "evangelism" or
any kind. I'm sure Rust is great. I just don't think it's news every time a
Rust fan says that its great.

YouTube lectures of people actually using it? That would be interesting.
Interviews of people actively porting a large C project to Rust. That would be
interesting.

Rehashing its feature sheet? Not so much.

~~~
dikaiosune
The author is actually using it, to actually rewrite portions of C projects in
it, and writing about their experience doing this in practice. For example,
the linked paper is about using Rust at the I/O boundary in an existing,
complex system.

~~~
youdontknowtho
Except that the article is actually just the feature sheet of Rust. I would
rather the article had been a link to that paper.

~~~
dikaiosune
uhhhhhh, multiple links in this block on the post:

"That’s because I have been working on this subject for a long time now:

* I did multiple talks on it * I even co-wrote a paper * I did it both as client and personal work"

~~~
youdontknowtho
Yes, I saw those links. The article that op linked to though was another Rust
feature sheet blog post, though.

This was days ago, why do you care?

------
nercury
While I am avid supporter of Rust, I can't help but feel this post is not
right. If Rust is so much better, why not prove it by writing software in
Rust, instead of words. After all, if Rust is so much better, the result will
speak for itself.

~~~
dikaiosune
In this case it seems the author is writing about the concrete results they've
already obtained, not about something abstract.

------
Const-me
Writing manually-vectorized SIMD code is the only way to approach advertised
performance on any modern CPU.

While there’s _some_ SIMD support in Rust nightly builds (but not in Rust
stable), it’s just not good enough. For one thing, Intel only supports their
intrinsics for C (C++ also gets that ‘coz compatibility) and Fortran. Another
thing, Rust strong type-safety concept complicates SIMD code, especially when
the code does integer math. These __m128i / __m256i / __m512i registers are
often treated as different data types by even consecutive instructions.

While Rust might be good enough for some areas, performance critical CPU bound
code is not one of them.

~~~
steveklabnik
Yeah, SIMD is being very actively worked on; until then, you can link in an
external assembly file, but that's not exactly the best UX.

~~~
Const-me
Yep, assembly’s not the best UX.

When you’re coding assembly, you have to code everything in it, including code
running on the scalar parts of CPU cores. With intrinsics you can code these
parts the usual way, e.g. for( size_t i=0; … )

With intrinsics you can even compose your vectorized code the usual way, VC
and Clang have __vectorcall calling convention for that, Intel has very
similar __attribute__((regcall)).

~~~
steveklabnik
Yup, absolutely. My understanding is that an RFC is currently being written,
so it's gonna be a little while to get into stable, but not forever. We'd have
liked to have had it sooner, but some stuff happened, that's just how it goes
sometimes.

------
mtgx
We need Rust to have great support for ARM microcontrollers before the IoT
industry really takes off.

~~~
biokoda
The relatively low portability of rust programs is definitely one of its
largest shortcomings.

~~~
bboozzoo
Can you elaborate? Although haven't tried it myself, I was under the
impression that it's quite possible to write low level code for at least for
x86 and ARM (and AVR?).

~~~
biokoda
You can write low level code. Tier 1 platform list is very short however.

[https://forge.rust-lang.org/platform-support.html](https://forge.rust-
lang.org/platform-support.html)

~~~
wtallis
It's probably unreasonable to expect a wide variety of IoT platforms to meet
the "automated testing" requirement for Tier 1 status.

------
w8rbt
If you are really worried about the security of old C code, then the safest,
most portable thing to do is re-write in modern C++. This is sane and doable
in a short period of time. Rust is not.

~~~
akavel
Oh no, no, no! Some people _may_ reasonably, with some extreme care and
diligence, maybe try to claim they can write safe and UB-free C code. If stars
align properly. (See maybe SQLite. Seen as one of the pinnacles of crazy
pedantism in C. Also, see the article about errors found recently in SQLite
with fuzzing!) But with C++, it's just impossible for a human. It's too big,
too quirky, too big, too wide, too deep, too tricky, too big, too complicated,
and also too huge. And purposefully (sic! for performance) sprinkled with UB,
which means barbed fences, trip-wire mines (to _boost_ your in-flight
performance), and hidden pit traps. Writing C++ code longer than a few lines
without UB is _humanly impossible_. Sorry. And that's just about UB, because
it's the most unpredictable thing; but there's still exception safety, for
example, which you also should be doing, which can "only" spoil your code's
logic (and then, obviously, go on writing over random memory, but that's
nothing uncommon for us C++ers). Is every line of your C++ codebase exception
safe? Yeah, sure. Good luck. And "modern C++" only means more C++. Because
backwards compatibility. Unless at some point some quest for a "safe subset of
C++" finally gains traction, which may maybe bring back some sanity. I do
really hope for that.

A personal opinion from a person who loved C++ for quite some time, before
diving deep enough to realize how dark is the abyss.

~~~
RcouF1uZ4gsC
Basically exception safety is a solved problem in Modern C++. If you use
RAIIike it was meant to be used, exception safety is not that big a deal.

~~~
tomjakubowski
True, I guess, for "business logic"-y classes. Not true at all for data
structures, particularly those that are templates.

~~~
AnimalMuppet
How is it not true for data structures? Even templated ones?

------
idyllei
I would love to learn Rust, but the learning curve seems a bit too steep for
me. The whole struggle with lifetimes is what has kept me from diving in. It
seems impossible to me to figure out how to use them correctly. Plus the
redundant syntax, I guess. If those things weren't an issue for me, I would
gladly try to learn Rust.

~~~
steveklabnik
Lowering the learning curve is a huge objective of this year!

> The whole struggle with lifetimes

How did you try to learn them? I'm interested in trying to make this easier!

> the redundant syntax

Which bits are redundant?

------
lj3
Has anyone done any in depth explorations of the safety and security of rust
code vs well written C or C++?

~~~
sqeaky
Well written the hard part there. I am riding on on the idiomatic C++11/14/17
train right now.

Life over here is great, but I can the warts of the language looking
backwards. The promised land in the future doesn't look quite as bright as the
promises Rust offers. The only thing keeping me from trying it is the giant
codebases in my current projects.

~~~
pjmlp
> I am riding on on the idiomatic C++11/14/17 train right now.

How do you see it?

Nowadays I only use C++ on hobby projects or when Java/.NET need some kind of
integration work.

To me it seems that as code bases grow organically, it will become very hard
to get which code is in which ANSI version, specially problematic given some
of the changes that took place between standard revisions.

~~~
sqeaky
At both of my jobs, I am working way too much, I am working in pre-C++11
codebases which now allow C++11. Both are working to for -Werror everywhere
and that helps a huge amount, and we can try to get more warning listed and
make broad but subtle changes to the codebases.

At one place we are in a race before a tight deadline and at the other we are
breaking a large monolith into smaller packages. At one place we implement a
new practice for all our code, and at the other we implement all our best
practices as we chop code out of the monolith. Both places are seeing a
reduction in bugs. The place with the packages is probably reaping a larger
benefit because they are aggressively re-working old code and have better test
coverage and can do so with confidence.

Code bases do grow organically, but like places they can be tended and pruned
to make impressive orchards or topiaries or left to the wild to become
impassible jungles. Tools like RAII, exceptions, classes, etc... are just
tools for managing this.

I think the tools in C++11/14 are at least as powerful as in any language
except perhaps Rust. Every time I hear a C developer say "C can be just as
safe..." I am imagine a person with nothing but a pocket-knife trying to
manage a whole forest.

~~~
pjmlp
Thanks for the overview.

------
kronos29296
Well Rust seems like a nice enough language if blog posts are anything to go
by. But I really cannot unless we something like RustPython and Rython to
replace CPython and Cython. (If anybody does write a new Python distribution
on rust I named if first)

~~~
cbcoutinho
Someone has already done the honors:

    
    
        https://github.com/shinglyu/RustPython

~~~
kronos29296
Should have seen it coming. Though it seems to be a one man show for now.

------
hbbio
You can also rewrite to C++, OCaml, Go... Each language has benefits and
drawbacks, and there is good reason for each them to exist.

Would your project benefit a lot from algebric datatypes -> OCaml is a great
fit!

You want to port as soon as you can: C++ is your friend.

You plan to write HTTPS service nodes: Go has the best standard library out
there.

~~~
lobster_johnson
If you read the article you'll notice that he proposes that Rust is a better
language because it allows incremental rewriting, rather than a complete one;
Rust doesn't have a runtime, doesn't have GC, and can fairly
seamlessly/painlessly integrate with C (you can both call and be called from C
easily).

That changes the equation considerably, compared to something like Go where
none of these benefits exists. For example, exporting code from Go not only
requires the runtime and GC, there's also significant function call overhead
because of the way Go maps goroutines to threads.

There are many other good languages, but few of them have these benefits. C++
is probably the option with the least amount of friction against C, but then
you're also inheriting most of the unsafety of C.

~~~
rbehrends
Incremental rewriting introduces the potential for new issues, as foreign
function interfaces are inherently unsafe.

Rust also has its own costs. Its biggest limitation when it comes to mixing in
code with that written in another language is probably its poor support for
shared ownership (i.e. multiple references of the same object), while in most
languages, shared ownership is the norm.

Multi-language setups (which you invariably get with an incremental rewrite)
also generally create other interfacing issues when they share objects. How
are you going to represent `std::shared_ptr<T>` or `std::vector<T>` in Rust,
for example? They are opaque types, after all. Can you guarantee that Rust
properly observes C++ semantics with regard to assignment, move assignments,
and destructors when dealing with instances declared entirely in C++ code once
you start mixing and matching C++ and Rust?

------
kreetx
Or even better, Haskell!

~~~
skybrian
If you rewrote only part of a large C++ project in Haskell, is there a way to
link it back in again?

~~~
aeorgnoieang
Call Haskell from C (not C++) seems pretty easy:

\- [Calling Haskell from C -
HaskellWiki]([https://wiki.haskell.org/Calling_Haskell_from_C](https://wiki.haskell.org/Calling_Haskell_from_C))

And the FAQ on the Haskell wiki directly addresses this too:

\-
[https://wiki.haskell.org/Introduction#I_already_have_a_large...](https://wiki.haskell.org/Introduction#I_already_have_a_large_application_in_C_or_C.2B.2B).

So apparently it _is_ doable!

~~~
antientropic
It pulls in a huge runtime system, though (including stuff like garbage
collection). This makes it much heavier than calling some Rust code.

~~~
marcosdumay
True, where "huge" means something around 10MB.

For many projects that is a showstopper.

------
snakeanus
Why Rust? Why not Idris? Why not Haskell?

Or you know, why rewrite it at all? You could instead try and use a safe C
implementation (such as gcc/clang with the sanitisers or something like
[https://staff.aist.go.jp/y.oiwa/FailSafeC/index-
en.html](https://staff.aist.go.jp/y.oiwa/FailSafeC/index-en.html)), but to be
frank if I was to rewrite my programs I would use a truly modern language like
the ones that I mentioned above.

~~~
Shriken
All of your questions are addressed in the article.

> Why Rust? Why not Idris? Why not Haskell?
    
    
        it can easily call C code
        it can easily be called by C code (it can export C compatible functions and structures)
        it does not need a garbage collector
        if you want, it does not even need to handle allocations
        the Rust compiler can produce static and dynamic libraries, and even object files
        the Rust compiler avoids most of the memory vulnerabilities you get in C (yes, I had to mention it)

~~~
snakeanus
All of these except GC also hold true for the languages that I mentioned.

As for GC, I think that Mercury avoids its use
[https://lirias.kuleuven.be/bitstream/123456789/131304/1/Mazu...](https://lirias.kuleuven.be/bitstream/123456789/131304/1/Mazur.pdf)

~~~
Rusky
Nope- C interop is higher on the list for a reason. Idris and Haskell do not
produce straightforward, runtime-less binaries that can be easily loaded by
e.g. CPython or Node, without any interference with _their_ runtimes.

------
grabcocque
Is this article "Use Rust" or "Stop using C"?

I mean both are reasonable arguments that get made often on here, but the one
doesn't necessarily follow from the other.

~~~
pm90
I think its a little bit of both. Primarily it is aimed at legacy software.
IMO not a lot of new code is written in legacy C, so we have a problem with
having a ton of dependencies written in a way that may not be the safest or
most reliable (from recent experiences). The authors suggestion is to rewrite
parts of those applications that tend to be buggy (mostly I/O parts) in Rust.

I really hate to see the naysayers comment on this article without even having
read it. I think the author made a very good point.

------
faragon
In my opinion, there are more reasons to rewrite Rust code in C.

~~~
ndr
such as?

~~~
faragon
E.g. ABI compatibility, multi-platform support, tools, open source code base,
community, etc.

~~~
fuwafuwa
ABI compatibility: Not really solved in C++ - see MSVC versus Mingw. If you're
coming from the standpoint of using a Linux distro or BSD you enjoy a
sanitized environment from the start.

Multi-platform support: Pray that your project's build system makes it
straightforward to get all the dependencies going on every platform. Pray that
your dependencies are easy to access and of the correct version. Pray that the
manufacturer's fork of an ancient gcc isn't too buggy to use. C made it
possible, not easy.

Tools: Both languages are too impoverished to have tooling as comprehensive as
the stuff available in, for example, Java, although VS pushes very hard on
this front. rustc already gives better error messages than any C++ compiler.

Code base: See the build system problem. Maintaining open source projects in C
and C++ is an exercise in endurance. Single header file libraries have come
into vogue as a result - and they're a hack. There is a lot of decent code in
both languages not being used because it's hard to get running.

Community: Rust is in a better position here in part because it's smaller. If
I encounter a C++ hacker in an online space it's just as likely to be an aggro
kid as a seasoned pro.

~~~
faragon
My point was explicitly about C, not C++. All these points are solved in C,
_decades_ ago.

------
Syzygies
Or write in Haskell in the first place.

Good Haskell code is rather fast, but that's beside the point. For many
projects, the only measure of goodness should be how nimbly one can then use
and experiment with one's code. In Haskell, like Lisp before it, one ends up
with a language to use one's code. Like writing a Mathematica package and
getting all of Mathematica for free, only better.

------
crb002
C can be safe if you use Turing incomplete libraries that have been verified.
It's the YOLO ad hock parsers that get you in trouble.

~~~
sqeaky
I am not sure if you are serious or not.

How does verification prevent buffer overflows? It can reduce their frequency,
but you can never know you got them all. They find issues in libCURL and
openSSL that have existed for years. Very few people are better than the
people working on those libraries.

It is better to be safe by default then add risk only when you need it. I say
this as a C++ developer, I think const should be the default state of all
variables and one should have to opt out of that, and Rust did that right and
C is so far from that I see no reasonable way to use plain old C safely. At
least with C++ I can hide my stuff in classes and verify one thing at a time.

~~~
snakeanus
Formal verification is not the same as a security audit. With formal
verification you can prove that something is bug-free.

> They find issues in libCURL and openSSL that have existed for years

Neither of them are formally verified.
[https://github.com/seL4/seL4](https://github.com/seL4/seL4) however is
formally verified.

~~~
sqeaky
Please, step away from "formal verification" and come back to world the rest
of us devs work in. I have never seen a formally verified piece of software
even once.

I don't know if its too hard to do to real software or if C doesn't allow for
it, or if it is just too expensive. The simple matter is that is not an option
for the vast majority of projects. Unless you can make it practical, I assert
it has no place in a practical discussion about preventing software issues.

~~~
AnimalMuppet
Worse: "It's amazing how many bugs there can be in a formally verified piece
of software." I forget who said it, but it's been a long time. (Maybe in
connection with formal verification of an OS? Does anyone remember?)

Formal verification only works if the formal verification was mistake-free.
(And how are you going to prove that? With a formal verification? It's turtles
all the way down...) Also, it only works for the aspects that were formally
verified. Heartbleed, for example, could be regarded as a flaw in the
specification; formally verifying that the code matches the spec won't save
you from that.

