
More Rust compared to C++ - rnestler
https://rnestler.github.io/more-rust-compared-to-c.html
======
danieljh
I'm bored of all the Rust vs. C++ comparisons, when the C++ code is something
you simply don't write with modern and idiomatic C++11 and C++14. Not that you
could not write it, but seeing this would not pass me reviewing the code.

The add_one example, as simple as it is, is a horrible example.

Why not embrace value semantics and write it like:

    
    
        int add_one(int x) { return x + 1; }
    

There is simply no need for pointers or references. Write simple code first.
To the heap allocation example:

    
    
        auto x = std::make_unique<int>(5);
    

Even if this is a silly example, just say no to unnecessary heap allocation.
Why not:

    
    
        int x{5};
    

Now to the lifted example:

    
    
        void add_one(std::unique_ptr<int> num) {
            * num += 1;
        }
    

This mixes operations with lifetime management. The add_one function does not
need to care about ownership. And it does not need to take ownership of its
argument. Instead on the call site do:

    
    
        int add_one(int); // from above
    
        auto p = make_unique(5);
        auto six = add_one(*p); // <-
    

There are valid points in the blog post and I know that most snippets are
meant as examples. But as someone writing C++14 you can't win me with those
kinds of posts, even though I'm having a fun time playing with Rust in my
spare time.

For more about modern practices see: [http://klmr.me/slides/modern-
cpp/](http://klmr.me/slides/modern-cpp/)

Also see Howard Hinnant's unique_ptr tutorial:
[http://howardhinnant.github.io/unique_ptr03.html](http://howardhinnant.github.io/unique_ptr03.html)

~~~
tekacs
You seem to be pretty fundamentally missing the point of using those examples.

All of your examples look almost identical in Rust and C++. That's not a
positive, nor is it a demonstration of similarity - it's a useless set of
examples for a post _demonstrating the differences_ between the two. :P

It's worth looking at each of the examples in the article and thinking about
them in the context of the more complex practical problem that do _require_
mutable/heap state.

Again, think of the examples as 'C-Reduce'd[1] ones - just enough for an
experienced programmer to 'get' the differences and infer its relevance to
real world cases.

To give at least one motivating example: for your first case (functional add
one) - that's entirely passable for the (tiny) int, but quite useless for the
larger case of BigInteger. That's where Rust's default move semantics would
really shine, but using BigInteger as an example would introduce lots of
irrelevant semantics, making the example bigger but scarcely more informative.

[1]: [http://embed.cs.utah.edu/creduce/](http://embed.cs.utah.edu/creduce/)

~~~
danieljh
I'm not missing the point of those examples. They showcase Rust vs. ridiculous
C++.

See my point with making the add_one function not taking a unique_ptr. This is
valid criticism and valid in more complex practical examples, too.

~~~
tekacs

        BigInteger& add_one(BigInteger& num)
    

is an awful function (really no checks of any kind, such as multiple holders).
You suggest that it's valid for more complex practical examples, but that's a
simple example where you'd probably want:

    
    
        void add_one(std::unique_ptr<BigInteger> num)
    

BigInteger is a common example often written in fluent/mutable style or to
take ownership of the argument in just this way.

~~~
danieljh
Your second function still mixes ownership with resource management. Taking an
unique_ptr<BigInteger> by value means, add_one takes the ownership of it. But
it does not return anything, making it a useless function.

The though-process goes like this:

* Do you need read-only access to the argument, take a const T&

* Do you need to modify the argument, take a T (by value), letting the call site decide to either pass an rvalue (move it, no copy), or an lvalue (copy)

There simply is no need for unique_ptr<BigInteger>, as BigInteger already
handles its resources internally (with move semantics). It's the same reason
as to why an owning pointer to a vector is silly when a vector already handles
ownership of its resources.

[http://klmr.me/slides/modern-cpp/#9](http://klmr.me/slides/modern-cpp/#9) is
exactly about this.

~~~
tekacs
Yup, I agree with you (and pjmlp, who I can't reply to) on all of this.

I can see where you're coming from in pushing for all-value semantics, taking
advantage of the mechanisms built into the language to control how those
semantics play out.

I think it's worth remembering through all of this that getting just the right
semantics requires a little more effort and thought on the part of the callee,
in C++-land, as well as which the relative lack of consistency (and
opaqueness) in these semantics, at least in the absence of a full IDE or
similar to jump to signatures.

Oh and briefly, the C++ may be unidiomatic, but it's perhaps useful as a C++
mimicry of the Rust code to show maximally similar semantics?

I think perhaps the dismissive nature of your GP post blinded me to exactly
what standard you wanted the C++ to hold to (oh and also, swapping out heap
allocation still seems to be missing the point, as there are definitely cases
where that's the 'right' behaviour)

------
rudolf0
It's pretty amazing how detailed and helpful Rust's error messages are.

    
    
        rust_dangling.rs:6:5: 6:6 error: cannot borrow `v` as mutable because it is also borrowed as immutable
        rust_dangling.rs:6     v.push(6);
                               ^
        rust_dangling.rs:4:14: 4:15 note: previous borrow of `v` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `v` until the borrow ends
        rust_dangling.rs:4     let x = &v[0];
                                        ^
        rust_dangling.rs:8:2: 8:2 note: previous borrow ends here

~~~
aikah
you got that right, I wish all languages did that,and why not, even suggest
modifications to the code in order for it to compile or run... Errors should
be an opportunity for programmers to learn, it shouldn't feel like a
punishment. But we'll get there eventually.

~~~
stormbrew
Obviously not so much for the kind of memory errors the GP is talking about,
but clang/clang++ do provide similarly detailed error messages, including
suggestions for name-related errors as to what you might have meant (either
because a different variable provides the member you specified or becuase of a
similarly spelled name).

~~~
pcwalton
clang was what I based the current Rust compiler's error messages on. Matching
its quality was a design goal from the start, since I think that one of the
biggest reasons for clang's success was the quality of its error messages.
Diagnostics are the UX of a compiler, and so they're deserving of the exact
same care that is put into mobile app or Web design.

~~~
stormbrew
Yes, absolutely. I have a theory that a big part of the reason clang
leapfrogged g++ so quickly on these things is because it was (initially aiming
to) dogfood itself, So getting better error messages than g++ could give was
an early and high priority task to its first users.

------
jarrettc
The author mentions that Rust's "Box type is an owning type" and compares Box
to C++'s std::unique_ptr. Worth noting: Rust's most basic variable syntax
provides unique_ptr-like behavior, so you usually don't need the Box type for
this behavior. For example:

[https://gist.github.com/jarrett/fe3f24c301a586efd7c1](https://gist.github.com/jarrett/fe3f24c301a586efd7c1)

~~~
SamReidHughes
Are you sure your comments are right? It seems to me like the value would be
memcpyed, when you pass by value. There is not one unit of memory that is
"taken over," or if there was, then Rust would have a serious problem.

~~~
jarrettc
> It seems to me like the value would be memcpyed, when you pass by value.

As far as I know, the compiler should not copy in that instance. Rather, it
should move.

> There is not one unit of memory that is "taken over," or if there was, then
> Rust would have a serious problem.

Could you elaborate on that? Rust does have an ownership model, and ownership
can be transferred as in the example. What sort of problems would you expect
that to cause? If you're worried that it will invalidate existing pointers,
the compiler checks that for you. Unless you deliberately circumvent the
check, the compiler guarantees that your pointers are valid.

~~~
steveklabnik
Semantically speaking, the only difference between a move and a copy is that
you're allowed to use a copy type afterwards, and you're not allowed to use a
move type afterwards. It's still a memcpy. Of course, these may be elided by
optimization passes.

~~~
jarrettc
> Semantically speaking, the only difference between a move and a copy is that
> you're allowed to use a copy type afterwards

Are you speaking about Rust specifically, or move in general? I had always
understood that move was no more expensive than passing by reference. That is,
I had thought the memory was on the heap and didn't need to be copied each
time someone new took ownership of that heap space.

~~~
steveklabnik
I mean in Rust.

> That is, I had thought the memory was on the heap

An example:

    
    
        let x = Box::new(5);
        let y = x;
    

While the 5 is allocated on the heap, when we move x to y, _the pointer
itself_ is memcpy'd. That's why Box<T> isn't Copy; as you say, a simple memcpy
won't actually duplicate the structure. Make sense?

(and in this case, I'd assume llvm's optimizations would realize the copy is
superflous and just elide it, but semantically, that's what's up)

~~~
jarrettc
Oh, agreed. But how about this:

    
    
        let x = BigExpensiveStruct::new();
        some_function(x);
    

That won't trigger a big, expensive memcpy of the BigExpensiveStruct, will it?
I'd thought that its memory was on the heap.

~~~
steveklabnik
If that's just a struct, it's stack allocated. So it's not ok the heap in the
first place. IIRC, LLVM may optimize passing it to the function by reference
though.

~~~
jarrettc
Good to know. Thanks! So I guess the moral is, if you have a big, expensive
struct, make sure the expensive part is in a sub-structure you _know_ is heap
allocated, such as a Vec. E.g.:

    
    
        struct Expensive {
            cheap_value: u32,
            expensive_value: Vec<u32>
        }
    

Does that seem like a good maxim?

~~~
SamReidHughes
That is not a big expensive struct (to memcpy). (edit: I'm just bickering
about what to call things, what you say is right.)

Your intuition about the performance of this sort of thing might be served by
reading about how calling conventions work. You might not need to copy the
struct from a local variable to where it belongs on the stack or in registers
when calling a function, either because the calling convention says you put a
pointer to the struct somewhere (depending on its size) or because you're (you
being a compiler) clever enough to put it in the place that'll end up in the
argument list later. The callee, however, has less flexibility, and if it
needs to pass the value somewhere else, it'll probably have to copy the data.
This is way better than allocating it on the heap -- stuff on the stack is in
the L1 cache, you compute it's location statically instead of having to
traverse data structures, but yeah if you found yourself copying around a
1000-byte struct you might want to box it or pass it by reference. I only know
about C and C++ calling conventions though, so don't infer from my comment
that Rust isn't doing anything "special" for big non-copyable structs -- I
wouldn't know.

~~~
Jweb_Guru
Rust (well, LLVM really) will automatically pass any "large" struct by
reference, which in practice is going to be anything more than a few words (I
think in the current implementation, it might actually be two). Unless the
function call is inlined, of course, in which case LLVM can do pretty much
whatever it wants.

------
Animats
Those trivial examples seem to be mostly to not scare off C++ programmers.
It's more interesting, though, to see how far you can push single ownership.
You can build a single-ownership tree. Rust lacks a "nil", but has
discriminated variant records (called "enums") to handle has/doesn't have
semantics. You can also grow a <Vec> of owning references, so you can have an
array which owns a variable number of children. Using all those features
together, you can build a tree. Take a look at the XML library for Rust to see
how this all works.

But it can't have backpointers. That would violate single-ownership. Weak
pointers for backpointers are available, but that's an unstable feature and
introduces reference counting overhead. Rust needs some construct which
includes a single-ownership forward pointer and a dependent back pointer,
updated together so that they're always in sync.

~~~
JoshTriplett
> Rust lacks a "nil"

Rust has the concept of a possibly-null pointer; you just have to write it
explicitly by wrapping something in the Option type. In particular, Rust has a
guaranteed optimization that says if you have a type with a single zero-
argument constructor and a one-argument constructor, and the one-argument
constructor takes a type that can't be null, then the type with those two
constructors will just use null to represent the zero-argument constructor. So
Option on a pointer/reference type just represents a nullable pointer.

~~~
Animats
Rust's "Option" type is a generic enum. There is no explicit "nil".

~~~
kachnuv_ocasek
That's what she said.

------
toolslive
To me, C++ suffers a lot from the "spoonful of sewage syndrome". Yes, C++14 is
a lot prettier than C++, but they preferred to be backward compatible with a
rotting language. If people know that some things are bad, but prefer to
retain them (for whatever reason), then they should not complain that at some
point somebody comes along and just drops the insanity. (yes, C++ would be a
lot better (or less worse) if they would correct mistakes from the past).

Anyway, Rust is simply a language with a lot less cruft, combined with a
better tool chain (compare rust compiler errors to g++ compiler errors).
That's not a surprise, considered C++'s age (30+ years). OTOH, without C++,
Rust would have been impossible. OTOH2, Rust wanted to be somewhere between
C++ and OCaml. I think they succeeded.

~~~
frik
C++11/14 should have been a clearer break from legacy ANSI C++. Overloading of
keywords just to avoid adding new keywords to the grammar (lexer/parser) -
that's what makes C++ really ugly. Especially if one has to read through old
third party code. The STL was so slow/bad that almost ever PC game shipped
with their own implementation. (I heard STL implementations got better). Boost
is too bloated, the compile time and executable increases and many avoid
adding it to their project.

~~~
pjmlp
We all know how clean breaks work out....

~~~
frik
Yes, we know.

But adding new keywords instead of reusing/overloading old ones for newer
features would have been nice. Writing new code in C++11 isn't the problem,
reading old ugly C++ code and trying to understand it is. I think the clean
break is Rust and it already looks promising. Sure some of its syntax is
influenced by non-C languages and looks a bit weird, but I can live with that.

~~~
pjmlp
I like a lot of C++ (since C++ARM days), but I also like safe systems
programming more (Ada, Modula-2, Oberon, ...).

The question is if Rust will earn the hearts of an OS vendor and be available
in their respective SDKs.

Right now I see Apple going Swift and Microsoft making .NET compile straight
to native code.

~~~
frik
Operating systems today are mainly C based (majority of kernel mode code) -
WinNT/ReactOS, Unix/Linux/*BSD/OSX/Android. Also in the user mode side, C is
heavily used in all above mentioned OS (WinAPI, DirectX, etc. are all in C).
C++ is also heavily used (e.g. for GUI applications like Office and newer OS
development). Most third party applications are written in C++ inclusive
almost video games of the last 15 years. Apple bought Steve Jobs' NeXT
Software and with it NeXTSTEP (now MacOSX) and Object-C.

I read somewhere that Microsoft is working on a Rust competitor and Swift with
it's great syntax is refreshing. But with these closed languages, I hope that
Rust has success.

Ada is great and heavily used in specific domains - only its syntax is not
that great. Also VHDL is based on Ada and its syntax. I had a Professor who
had worked on Modula&Oberon with Niklaus Wirth, though he later moved on to
Sun Java.

~~~
pjmlp
> Operating systems today are mainly C based

Only if you are speaking about UNIX clones.

Genode, BeOS, Symbian and OS/400 are a few examples of OSes developed in C++.

> C is heavily used in all above mentioned OS (WinAPI, DirectX, etc. are all
> in C)

DirectX is COM and most new WinAPI since Vista are also COM.

No one in his right mind uses COM from C instead of C++.

Windows is moving into straight C++ as of Windows 8, with the adoption of
kernel support for C++ based drivers and the move into WinRT, a COM superset
coupled with .NET metadata.

The upcoming Windows 10, embraces WinRT for its Universal API model and C
style is considered legacy. Hence why Microsoft is not caring to update their
C support beyond what is required by the ANSI C++ standard.

Good luck coding COM and WinRT applications with pure C.

Mac OS X drivers are written in a C++ subset, Embedded C++.

Now imagine, if it has taken this long for C++ to start replacing C in core OS
development, how long it will take for Rust to fill in?

Rust is nice, but I am too long in this industry and have seen too many
languages come and go in systems programming. The only ones that stayed around
have had OS vendors support in their respective SDKs.

~~~
frik
> Only if you are speaking about UNIX clones.

I wrote "operating systems today are mainly C based" and that's true. Also
WinNT/ReactOS was inspired mainly by VMS and OS/2\. I am well aware that many
other OS exist.

> DirectX is COM and most new WinAPI since Vista are also COM

Fundamentally DirectX and the (post Win95a) shell is COM-based API, and while
COM may look on the surface as if it is C++ based, it's actually not - a COM
interface isn't a class, it's a struct, and full C compatibility exists (COM
as-designed is intended to be language-neutral). And major parts were coded in
C. I suggest checking out the Wikipedia article on COM for further info.
Coding against COM from C may not be as simple as from C++, but that doesn't
matter. dotNet and their new WinRunTime are also COM-based. Sure, it's clear
that MS is using C++ nowadays and nothing is wrong with that. The real
question is what the future of C#? Will it be 'replaced' by a new language
similar to Swift and Rust? Or maybe it will get a static compilation option
similar to Go-lang that contains the GS and parts of the framework in the
executable. Microsoft has little C# based software on the client side - due to
the massive setback and fail of the original "Longhorn" where the new C# based
Explorer and WinFS beta1 were way too slow. Nowadays C# based server side
product development is mainly done by MS India (e.g. SharePoint). So as it
seems they are back to native WinRT (COM based) and HTML5.

> Rust is nice, but [...] The only ones that stayed around have had OS vendors
> support

Dogfooding is important and Rust is already used in the Mozilla Servo project,
a new web engine. One can imagine that a future FirefoxOS will be based on it
as well (replacing the C++ based Gecko engine), as it already uses no legacy
XUL GUI code. And there are Rust based experimental operating systems like
from "Julia Evans": [http://jvns.ca/blog/2014/03/12/the-rust-os-
story/](http://jvns.ca/blog/2014/03/12/the-rust-os-story/)

~~~
pjmlp
> I suggest checking out the Wikipedia article on COM for further info.

I know COM since it was called OLE and promoted as an DDE evolution.

Developing software since 1986.

> Or maybe it will get a static compilation option similar to Go-lang that
> contains the GS and parts of the framework in the executable.

It already has that since Singularity, via Bartok, which was adopted into
Windows Phone 8 via MDIL, which produces native binaries with dynamic linking.

[http://channel9.msdn.com/Shows/Going+Deep/Mani-Ramaswamy-
and...](http://channel9.msdn.com/Shows/Going+Deep/Mani-Ramaswamy-and-Peter-
Sollich-Inside-Compiler-in-the-Cloud-and-MDIL)

Static linking Go style is coming with .NET Native.

[https://msdn.microsoft.com/en-
us/vstudio/dotnetnative.aspx](https://msdn.microsoft.com/en-
us/vstudio/dotnetnative.aspx)

------
halayli
Unfortunately most of the C++ vs Rust comparison posts are coming from people
who aren't familiar with C++'s idioms and it's obvious that it's not their day
to day / goto language.

------
aliakhtar
Is there anything that compares Rust's performance vs other languages?

~~~
steveklabnik
Like all performance questions, the answer requires another question: "at
what?"

There's been a number of different benchmarks, in a bunch of places. We
generally end up in the same realm as C++, sometimes faster, sometimes slower.

Also, many benchmark authors seem to accidentally compile Rust code without
optimizations. We're considering switching Cargo's defaults because of this.

~~~
aliakhtar
I'd be interested in seeing Rust included in
[https://www.techempower.com/benchmarks/](https://www.techempower.com/benchmarks/)

~~~
steveklabnik
Yeah, it might be neat. It's still unclear how good Rust is going to be at
website backends, though there's a lot of people putting hard work into it.
Once it all matures, seems good.

~~~
aliakhtar
I'm considering C++ for an NLP related project. Normally I use Java/Scala, but
the memory usage when you have to store a lot of strings, is pretty high. (The
Stanford NLP library recommends at least 3-4Gb of Ram)

I'm considering C++ for the lower memory requirements, but obviously the
syntax, etc aren't ideal. Do you have any opinion on how Rust would perform in
that scenario?

~~~
steveklabnik
I'm not mega familliar with NLP as a field, but abandoning Java means you
abandon Weka, right? While Rust the language might be good for this, there's
basically zero libraries, so you'll be doing all that work from scratch.

------
fiatjaf
More Rust compared to C++:
[http://rosetta.alhur.es/compare/C++/Rust/#](http://rosetta.alhur.es/compare/C++/Rust/#)

~~~
steveklabnik
This Rust code is very, very, very out of date. There's a project to update
things to a more modern Rust: [https://github.com/Hoverbear/rust-
rosetta](https://github.com/Hoverbear/rust-rosetta)

~~~
fiatjaf
Why aren't these tasks being updated at RosettaCode?

~~~
steveklabnik
I believe they were waiting for stability so that they wouldn't introduce
churn. Unsure though, I don't really participate in that project.

------
dmitryskiba
Are there examples where borrow checker stands on your way? Like the mentioned
(in comments) way of building a tree. I'm sure there should be really weird
edge cases.

~~~
steveklabnik
Newcomers to Rust often find that the borrow checker _always_ stands in their
way, because they haven't internalized the rules yet. I've also spoken with a
few frustrated ex-C++ers, who have some sort of pet pattern that works
basically all the time, but the borrow checker doesn't like the 'basically.'

Another good example is that the borrow checker is currently based on lexical
scope. See all the examples in [https://github.com/rust-
lang/rust/issues/6393](https://github.com/rust-lang/rust/issues/6393) .

In general, any kind of analysis like the borrow checker will reject some
valid programs, as it pays to be extra conservative. You then slowly expand
the set of accepted programs, until hopefully, it matches the true set of
valid programs, without accepting invalid ones.

~~~
JoshTriplett
> I've also spoken with a few frustrated ex-C++ers, who have some sort of pet
> pattern that works basically all the time, but the borrow checker doesn't
> like the 'basically.'

What kinds of pet patterns have you seen?

~~~
steveklabnik
It's hard to remember specifics, as this is usually based off of someone
jumping into IRC and asking a question about how a certain thing works.
They're often very detailed things that require intimate knowledge of
implementation details. Or threads like
[http://www.reddit.com/r/rust/comments/31mgav/what_would_the_...](http://www.reddit.com/r/rust/comments/31mgav/what_would_the_rust_equivalent_of_this_c/)
, which isn't really _wrong_, but unions are hard, and so people miss them.

This kind of thing happens any time you transition languages: when I started
hacking on Rust, I was trying to figure out how to do the metaprogramming
shenanigans that Ruby lets me do. Ironically, Rust's metaprogramming features
are my biggest weakness as a Rust programmer, I don't really write my own
macros.

------
hzhou321
Lately I have been thinking that we are getting the abstraction wrong in
programming.

In real world, abstraction is a lossy process. From "white horse" to "horse",
we lose the information about color. And this lossy nature of abstraction is
important, as it become simpler. This should be differentiated from saying
"horse" but actually meant "white horse", because in that situation, it is not
abstraction but omission. The complexity is still there (or even more).

In programming, add types or mutability increase the complexity. In that
regard, Rust is simply more complex. But that is not what I commenting on. I
am commenting on the trend in programing to hide that complexity by omission
(such as type inference) or make it implicit such as using key word 'let' and
'var'. They only make the user less aware of the complexity at surface.

Now I am not saying language designers are not doing the best they can do
(earn both honest and popular credits); but I have been thinking what might be
the ideal way to handle complexity, and I only can see how we are doing it
with our natural language. In our day to day business, when we say horse, we
do not care about its color, and any one who interprets the sentence (with
horse) is fine to imagine either a white horse or black horse or any but need
understand the color is not important. So in analogy, in high level
abstraction, we should be allowed to say a number with no type but be
understood by the compiler that any number will do, which may even include a
byte. Then the language should allow us to declare specific type as well --
just like in real life we should be able to directly say "white horse" if the
color is meant to be important. Which means we should be able to specify an
integer with 64 bytes long (I don't think we ever really need arbitrary
precision) if we deem critical. We should note that I am not referring a
dynamic type, nor an optional type system. The type is always specific at very
low level (cpu level). The compiler decides for us when we don't say and we
don't say when it is not important any compiler decision is ok.

Of course this is not realistic and the reason is simple. The way we do daily
business leads to imprecise implementation. I say horse, and it can be
implemented either as a white horse or black horse or anything in between
(hopefully a common one at least). It is tolerated in our daily life because
the implementation is often cheap and can be directly verified at every level.
E.g. I say get me a horse, I verify your facial to see if you heard, and
verify you action to see if you are about to act, and I get in between gossips
to get warnings of any surprises, ..., all the way until I actually have the
horse. And along any of these steps, if I find I actually meant a white horse
and you are not getting a white horse, I interrupt your implementation and
make corrections. In programming, writing code is expensive (programmers are
paid well, takes long time), and the typical only feedback is when the code
completes and the user uses the software (at which point the user is getting
the feedback, not programmer), and when things go wrong, user have no idea how
to make correction, and this is even true for programmers, who have no idea
what is inside their libraries and the nuances of the programming languages
they are using.

TL;DR We are stuck.

To solve this scenario, I think the first step is to make every one be able to
program, then the implementation can become cheap, and the feedbacks can
become short and quick, and the compiler can become less rigorous, and then we
may be able to program like we speak -- program the way we thinks.

~~~
frik
We could write down our problems in simple English.

We then use do a natural language parsing (NLP), and transform the output to
Prolog code. We execute the Prolog code and use a search engine to find the
solution. Each result is used an input for another (second) search step. We
score each result and _do something_ with it. That's more or less how IBM
Watson works.

~~~
matt_d
So, like
[http://gkoberger.github.io/stacksort/](http://gkoberger.github.io/stacksort/)?
;]

