
Comparing Rust and C++ - skazka16
http://kukuruku.co/hub/rust/comparing-rust-and-cpp
======
claudius
It might be a good idea to compare C++ and Rust and not “C with Classes and no
use of compiler options” and Rust. They do have a fair point about ugly
template error messages, but the remaining issues are mostly moot: Freed
memory issues can be avoided using std::unique_ptr and its siblings, lost
pointers to local variables shouldn’t occur when using references instead of
pointers, uninitialised variables are warned against when compiling with the
(hopefully standard) -Wall -Werror, implicit copy constructors can either be
deleted or this particular issue can be avoided by using a unique pointer
(which has no copy constructor, hence the enclosing classes’ copy constructor
is also deleted), I don’t quite get the issue with memory overlap, which is
mostly an issue of a function receiving the wrong number set of arguments for
the way it is written. The bit about broken iterators is actually nice, but in
this case could be avoided by handing an external function two const_iterator
which then cannot be changed. I don’t get the problems with "A dangerous
switch" and "A broken semicolons", both seem sensible constructs and if you
have bugs, you have bugs. Multithreading works as expected, except that using
C++11 <thread> would probably have been nicer and one would have to explicitly
make e.g. the lambda passed to std::thread mutable.

All in all, yes, Rust seems to be a nice language and in particular the
template system looks somewhat more understandable, but please don’t write
"Comparing Rust and C++" when you’re not actually using C++ but some weird
mishmash of C and C++. If there are new, delete or pointer arguments in
functions in your code, you’re most likely doing it wrong.

~~~
gilgoomesh
> Freed memory issues can be avoided using std::unique_ptr and its siblings

You can use a std::unique_ptr after it is assigned to something else. That's
basically a use after free.

> lost pointers to local variables shouldn’t occur when using references
> instead of pointers

Lost references to heap variables happen all the time in C++ when the target
is deallocated before the reference is accessed. It's the same problem and
Rust fixes it the same way.

> I don’t get the problems with "A dangerous switch" and "A broken
> semicolons", both seem sensible constructs

Really? Then you're being naïve or obtuse. Both are common sources of bugs in
C/C++. Accidental failure to handle all inputs (common when additional inputs
are added after the handling code is written) and subtle typos with major
consequences (see Apple's "goto fail" bug).

The purpose of Rust is to eliminate causes of careless and accidental errors
by forcing you to do things the right way every time (rather than permitting
lazy code).

~~~
pjmlp
> You can use a std::unique_ptr after it is assigned to something else. That's
> basically a use after free.

While true, you can make it throw in such scenarios.

I wouldn't be surprised if the debug builds of modern C++ compilers wouldn't
do it already.

~~~
mike-cardwell
Here is some example code showing the problem:

[http://pastebin.com/6wu7bcrF](http://pastebin.com/6wu7bcrF)

I compiled it using both:

    
    
      g++     -std=c++14 -Wall -Wextra -g test.cpp -o test
      clang++ -std=c++14 -Wall -Wextra -g test.cpp -o test
    

GCC v4.9.1 and Clang v3.5.0.

Under neither case did it supply any warnings at compile time. In both cases
it segfaults when it hits the second std::cout whilst running.

~~~
tomjakubowski
Since that's undefined behavior _anything_ (including and especially things
worse than a segmentation fault) could happen.

------
Matthias247
These biased bashes against C++ that advertise rust get somewhere between
boring and annoying recently. Yes, Rust has some interesting safety features
that prevent some issues that you can run into with C++ (but not all). And
yes, most of us will agree that header files suck and the rust approach here
is nicer. That template error message are also PITA is also well known - but
on the other hand templates are different (and more powerful) than generics.

On the other hand there are still a few constructs that I'm using using often
enough in C++ that have currently no sane representation in Rust and that are
astonishingly never mentioned in these articles:

\- Everything that you use the friend keyword for. In Rust there is no way to
access private data defined in other file (because that will automatically be
another module which is also something I disagree with).

\- Most things related to inheritance and interfaces. Composition is not
always a better solution and Rusts traits seem still more suited for compile-
time polymorphism than to runtime polymorphism. As an example try to find the
equivalent of a `shared_ptr<SomeThing> thing(new SomeThing());
shared_ptr<ISomeThing> iface = thing` in Rust. And yes, I want to be able to
use both afterwards.

\- __Enabling __concurrency. Some people might disagree but I think Rust is
currently mostly good at preventing multithreading issues - but not at
providing good and easy ways to enable it. E.g. something like boost asio
would be between hard to impossible to implement in Rust because the safety
mechanisms disagree with callbacks that mutate state or Future objects (which
are similar to callbacks). For some things using low-level primitives like
channels (aka queues) and creating new threads might be fine, but there are
also enough cases where you would prefer a simple singlethreaded eventloop.

All in all I think there are still enough areas where I think Rust still is
(unfortunatly) no real alternative for C++. And as long as that's the case I
think writing such biased articles isn't fair.

~~~
ekidd
_These biased bashes against C++ that advertise rust get somewhere between
boring and annoying recently._

The main Rust community tries to discourage Rust "zealotry." For example, on
/r/rust, the rules include:

 _4\. When mentioning other languages, keep the discussion civil. No
zealotry!_

Anyway, as for your other question:

 _E.g. something like boost asio would be between hard to impossible to
implement in Rust because the safety mechanisms disagree with callbacks that
mutate state or Future objects (which are similar to callbacks)._

I know that futures have been implemented several times in Rust, including in
the standard library:

[http://doc.rust-
lang.org/nightly/std/sync/struct.Future.html](http://doc.rust-
lang.org/nightly/std/sync/struct.Future.html)

In general, multithreading primitives can be implemented using ordinary Rust
libraries. Internally, these libraries use _unsafe_ to do low-level threading
work, but externally, they provide a safe API. The library author takes
responsibility proving that their design is, in fact, thread safe.

If you know of specific boost asio features that can't actually be implemented
safely in Rust, I'd love to know about them. I know that various people want
to work on async I/O libraries for Rust after 1.0, and it would be nice to
know about any major obstacles in advance.

~~~
Matthias247
Regarding the futures:

These are futures that you only can synchronously wait on (like what is
currently available as std::future). However there is no possibilty to attach
continuations that run when the value of the future is available. E.g. like
the C++17 concurrency proposals, C#'s `Task` type or Javas
`CompletableFuture`. These all rely on storing some kind of callback or
closure inside the Future. That's not easy in Rust because it leads to
situations where ownership is no longer obvious. The continuation might need
to capture objects that are still needed locally (so you need something
similar to Rc<RefMut<SomeThing>> ). In the most simply example's the
continuation needs a mutable reference to the Future itself whereas the
Future-producing operation also has one. And if you have possibilities that
the continuation could run on another thread/executor than the calling thread
(which is possible with the other languages) things get more complicated.

Regarding asio. You don't have to deep dive very much here: Simply consider
boost::asio::async_write(socket, buffer, [](error_code, bytes_transferred) {
/* Do something with the result */ });

You need some mutable objects (like socket or the object that contains socket)
locally AND you need acccess to them in your continuation. This will only work
"safely" with garbage-collected or ref-counted types and these do still not
seem to work for interfaces.

~~~
ekidd
_This will only work "safely" with garbage-collected or ref-counted types and
these do still not seem to work for interfaces._

I'm not quite sure if I understand the problem you're describing. I can
definitely share mutable objects between threads, and I can do so using
abstract traits (aka "interfaces"). Here's an example:

[https://gist.github.com/emk/acdf3ab9c79ba1abe6d2](https://gist.github.com/emk/acdf3ab9c79ba1abe6d2)

I had to use a "box" here, because that's the easiest way to store objects of
varying size that implement a specific interface. Experienced Rust developers
may be able to simplify this a bit.

Is this what you were thinking about? Or did you mean something else?

~~~
Matthias247
The part that your example is missing is that one side uses the trait object (
_Common_ ) whereas the other side uses the "normal" type ( _Foo_ or _Bar_ ) or
another trait object view of the object. And the be able to cast between such
"smart-boxed" types after you acquired one of them.

See the disucssion a little bit below for examples with _Rc_ (it's the same
problem but within a thread instead of between threads).

------
kibwen
I've flagged this because not only is it an unfair characterization of C++,
it's also using a version of Rust that is six months out of date.

As a Rust fan, I don't think we need to resort to cheap shots. The language
stands well enough on its own; misinformation and propaganda pieces won't get
us anywhere.

~~~
Dewie
If anything, the Rust community seems more concerned with only showing the
good sides of its community than it is with only showing the good sides of
Rust. That of course has its social/"political" advantages.

~~~
kibwen
I see this as a technical advantage, not a political one. I want the language
to be legitimately great, which means that we need to encourage an atmosphere
where people feel welcome to express informed and constructive criticism.
Breathless praise is surely kind, but it doesn't really advance the
conversation. In other words: I already know that the language is good; I want
you to tell me how it could be better.

------
shmerl
Raw pointers is a bad example to compare to Rust. It's more like comparing C
to it. If anything one should compare managed pointers like std::shared_ptr
and etc, especially since the author mentions C++11.

~~~
TillE
Also failed to use std::thread and its related synchronization primitives.

And nobody who's been writing C or C++ for more than five minutes would
actually make that mistake with a switch statement (omitting breaks). On the
contrary, its fall-through behavior is often very convenient.

~~~
jzwinck
I missed a break in a switch a few weeks ago. I have been programming in C++
for more than a decade.

------
nly
Comparing languages with constraints which were laid in bedrock 30 years apart
is fairly uninteresting. C++ is constrained by its compatibility with C, and
always will be. Even C++17, which will be the biggest shift in the language
ever, won't change this.

I see very little evidence that most programmers actually care about the
advantages Rust _the language_ , as specified today, brings. I'd contest that
these days the biggest reason competing languages like Go, Rust, Python, PHP
and Java often trump C++ is less to do with the language, and more to do with
these languages having sizeable and fairly decent standard libraries.
Programmers want to get shit done.

The C++ standard library is _tiny_ and has seen fairly paltry growth over the
years, with only modest introductions in C++11 of things like regex and
threads. If a set of filesystem, HTTP, networking, and serialization libraries
were part of the standard, and well implemented, we may well see a different
landscape.

~~~
skriticos2
It's tricky and there is no one-language-to-rule-them-all because they have
up- and downsides.

C++ has the advantage of an incredible amount of code written in over the
years that mostly works. It also executes fast. It's also easy to find
developers for it. With the new addons to the specification it's even possible
to write readable code with it.. However, it has a stupidly long, verbose and
feature creepy standard (well over 1000 pages), which results in no developer
reading it. Because of it's design, compilation times tend to take long (as a
ridiculous amount of headers are included even in the most simple
applications).

Go is pretty much the opposite. It has a lean and readable spec (on purpose)
and a fairly common canonical syntax without much discussion about it. Because
it's relatively new, there is not much written in it however. It has some
specific strengths, like a concurrency implementation that abstracts most of
the process/thread/mutex stuff. With this, it does appeal to new projects that
don't depend on libraries written in other languages. There is no point in
porting multi-million SLOC applications to Go though, as that would be
incredibly time-consuming. It has the potential to surpass C++ in a few
decades though, as it operates in the same domain (systems programming).

Python is a versatile scripting language. It has nice syntax and is incredibly
flexible at manipulating data. Add to it the well documented, feature-rich
standard library. With it you can evaluate data with incredible flexibility
and it has a fairly complete feature set for general programming tasks that
span multiple OSs. The downside is that it's slow in execution and needs the
interpreter along the scripts. Especially for smaller tasks and simple
automation it is the first choice however because of it's small footprint in
constraints (you can just edit and drop a scriptfile anywhere on a Linux
system and it works).

Java.. I'm not entirely sure what Java does well. I hear praise for the JVM,
but generally I get the impression that it is plagued by legal issues, alien
looking GUI tools, more than verbose syntax and it also has a stupidly long
specification (almost half of C++'s). Maybe someone can shed light on this
one. Tools maybe?

~~~
nly
The C++ language specification is only ~450 pages, the rest of the ~1200 or so
pages is the standard library specification.

> Java.. I'm not entirely sure what Java does well.

* Package/module system is dead "drop your JAR here" simple.

* Humongous standard library.

* Oracle provide a really powerful free JIT (probably still one of best for real world performance when it comes to JITing static languages)

* Standardised language specification (like C and C++)

* A well specified, really simple bytecode that other compilers and runtimes can target.

* It's a newbie friendly, procedural, object-oriented language that encourages 'patterns' that make developers feel comfortable and that they know how to tackle problems (whether it's the right way is another matter)

* Relatively safe, meaning there is less to learn (no pointers, memory management, arithmetic overflow etc).

------
sspiff
While I love what Rust is trying to achieve and hate the many, many issues C++
has, this reads like a blatant propaganda piece.

It's not comparing C++ with Rust, or at least not fairly. It's just listing
the problems of C++ that Rust is trying to solve. Someone well versed in C++
could easily write an article with the same title and list a dozen reasons why
C++ is superior.

~~~
iopq
It's not meant to be fair. Rust is created as a successor of C++ to completely
overtake it everywhere.

~~~
sspiff
Yes, but everyone knows that it won't do that, and anyone who doesn't have
brain freeze from the Rust Kool Aid understands that in programming languages,
it's never so clear cut.

I realize that Rust was designed to address the issues with existing systems
programming languages, but surely it has flaws, tradeoffs and small
annoyances, like every other programming language.

I really like Rust and see the value in what they are trying to achieve, but
I'm realistic enough that it won't be perfect.

~~~
steveklabnik
> surely it has flaws, tradeoffs and small annoyances, like every other
> programming language.

Rust core team member here: it absolutely does. Rust is _far_ from perfect, as
much as I love it. Anyone who tells you Rust has no flaws is lying.

~~~
mholub
Can you describe in few words which flaws do you see in rust?

------
halayli
One of the main problems of C++ is that programmers treat it as C with classes
because that's what it feels like at the surface. They think they know it
because it feels like C and many decide not to learn its patterns and details.
From the examples given, it's clear that the author is not very familiar with
C++ and is also comparing Rust to C++98 which is not fair. C++11 is almost a
new language.

------
wfunction
How does Rust handle multiple object files and dynamic linking? It seems to me
that pretty much all of these guarantees break down if the compiler can't see
the source code for the whole program at once.

~~~
adrusi
All the information needed to make these guarantees is the type information of
all visible items in a translation unit ("crate").

~~~
wfunction
I'm skeptical. Did they really make the type information stored in the ABI
_that_ advanced? Storing that kind of information outside of name mangling is
a nontrivial feat (i.e. they'd need to rewrite the linker) and there's not a
whole lot of information you can pack into name mangling.

~~~
adrusi
It basically generates a header file from the source code and then stores it
alongside the object data containing the type information of all public items.

~~~
wfunction
So there's no run-time dynamic linking? Or is it possible, but only with
additional metadata files present? Or maybe the assumptions are statically
verified according to the interface and it is assumed that they are never
broken by clients?

~~~
Rusky
The last one. Types are stored as metadata in the object files, those types
are verified at build time, and then assumed correct at run time.

~~~
wfunction
Gotcha, thanks!

------
moomin
I'd actually much rather read the opposite article: things that C++ excels at
that Rust falls down on. I'm not an expert, but I think there's still
allocation use cases C++ is better at.

~~~
humanrebar
Off the top of my head:

* C++ has more robust compile-time meta-programming facilities, at least for the time being.

* C++ can generally include C (well, C89) code as-is with no translation layer. Rust's FFI is pretty inoffensive, but it's more work than a #include

* It's easier to do the really dangerous stuff if you have a good reason to. You _can_ actually call push_back on a std::vector without invalidating your iterators if you're careful. I've never seen it used properly in production code, but it's possible. At any rate, it might be more awkward to write equivalent code in Rust. Then again, maybe a deque is better than a vector for this use case, in which case I suspect that Rust is capable of a more optimal solution than push_back or std::back_inserter.

~~~
steveklabnik
C++ can also easily bind to libraries written in C++, while Rust cannot.

(those libraries need to expose a C interface for Rust to be able to)

~~~
humanrebar
That's true, though I blame C++ for its lack of a standard ABI more than I
would blame Rust. Not that that helps if you're just trying to get work done.

------
joelthelion
Rust looks really promising. Unfortunately, the tooling just isn't there yet,
it seems. Release a working plug-in for YouCompleteMe and I will switch
overnight ☺

~~~
steveklabnik
It's true that Rust's tooling is lacking, but if you're just looking for
autocomplete for vim, [https://github.com/phildawes/racer#vim-
integration](https://github.com/phildawes/racer#vim-integration) exists. I
haven't tried it myself, but I hear good things.

------
frozenport
Does UB enabling code optimization? The 'dangerous switch' might run faster if
the compile deduces that only two of the possibilities are valid?

------
shin_lao
If I understood correctly, it is not possible to manually manage memory in
Rust and do things like memory recycling or pools?

~~~
seabee
No, you can do that, and can do so safely (if you implemented the unsafe parts
correctly, at least). For example, here's an arena allocator crate:
[http://doc.rust-lang.org/arena/](http://doc.rust-lang.org/arena/)

~~~
EpicEng
>No, you can do that, and can do so safely (if you implemented the unsafe
parts correctly, at least).

That is equally true of C++; you're completely safe as long as you don't make
any mistakes.

~~~
seabee
The entire C++ language is an unsafe block. I think you're trying to be funny,
but you missed the point.

A concrete example of safety is that Rust can tell you if you reference memory
from an arena after the arena goes out of scope. C++ can not.

------
towelguy
The one thing I don't like about Rust is the implicit return. Looking at code,
it's easier to spot a missing/extra "return" than a missing/extra ";".

~~~
Dewie
If you put an "extra" (unintended) semicolon on the last expression, the
function will return Void/Unit: if this doesn't agree with the given type of
the function, then there will be a compiler error. If you forget a semicolon,
then the type of the function will become the type of the last expression,
which will again make the compile complain.

Are there other issues that you had in mind?

~~~
towelguy
The C++ will complain as well, the issue is ";" is 6 times less characters
than "return" and so, easier to overlook.

~~~
Dewie
I think I'd rather have that problem than this problem:

    
    
        if (cond); //oops, a semicolon
            alwaysHappens();
    

, thank you very much.

