
Why Rust? [pdf] - nercury
http://www.oreilly.com/programming/free/files/why-rust.pdf
======
SloopJon
While I appreciate the direct link that bypasses the email form, here's the
page describing the report and its author:

[http://www.oreilly.com/programming/free/why-
rust.csp](http://www.oreilly.com/programming/free/why-rust.csp)

"One of the original designers of the Subversion version control system, [Jim
Blandy is] a committer to the SpiderMonkey JavaScript engine, and has been a
maintainer of GNU Emacs, GNU Guile, and GDB."

------
hrjet
From page 19:

> But all those other languages include explicit support for null pointers for
> a good reason: they’re extremely useful. [....] The problem with null
> pointers is that it’s easy to forget to check for them.

There is no inherent reason for that; just that mainstream languages which
support `null` haven't been checking `null` usage. Some of the recent ones do.
For example, Kotlin has non-nullable types by default, and null types have to
be explicitly marked and checked for null-ness.

Even for Java, null analysis is built into Eclipse with the help of
annotations. Though, ofcourse, it would be far nicer to have it baked into the
language.

~~~
dllthomas
A null pointer is a perfectly sensible implementation of an optional type.

~~~
Veedrac
It's different from a traditional Option type, though, in that
Option<Option<T>> is inexpressible, and that the distinction between map and
flat_map is reduced.

~~~
AnimalMuppet
> It's different from a traditional Option type, though, in that
> Option<Option<T>> is inexpressible.

I don't think so. It's just a pointer to a pointer, rather than a single
pointer. That is,

    
    
      int **value;
    

and either

    
    
       value == null
    

or

    
    
      *value == null
    

or

    
    
      **value == some value of interest

~~~
tomp
Hm... not really.

Say you have a function, `f : (bool, T) -> Option<T>`.

    
    
        function f<T>(cond : bool, value : T) : Option<T> {
          if cond {
            return value                 // equivalent: Some(value)
          } else {
            return null                  // equivalent: None
          }
        }
    
        let x = f(false, f(false, 1))    // equivalent: None
        let y = f(true, f(false, 1))     // equivalent: Some(None)
    

There is no way to tell apart `x` and `y`.

~~~
dllthomas
You are confusing types and representations, and your function is not well
typed.

~~~
tomp
> You are confusing types and representations

Hm... not sure if I am. Or, at least, `null or T` is not a valid
representation for type `Option<T>`. It's all very confusing.

> and your function is not well typed.

How so?

~~~
dllthomas
The `value` you are returning has type T. T is not the same type as
Optional<T>.

With AnimalMuppet's approach, you would need to return a pointer to a T (so,
`&value` in something Cish). Note that this is _not_ the same thing as a
nullable reference in, for instance, C#.

`null or T` is a valid representation only when T is a non-nullable reference.
It doesn't work when T is optional, and it also doesn't work when T is a
primitive type, a struct, &c.

That said, there's obvious reasons it might be wanted an optimization where
it's possible. For that specific case, `Just value` could be specialized to
`value`, but that can be done by the compiler (akin to automatic unboxing,
elsewhere).

------
Animats
The book is exactly what it says it is. "Why Rust", an argument for Rust. 50+
pages. It's a fun read, but not too useful. My own comment on Rust is that the
borrow checker is brilliant, and we'll see that again in other languages. The
type/generic system picks up where C++ left off, and is very complex. But that
may get better as the mess settles down and gets better documentation.

We now need a "programming in Rust" book which is not by one of the Rust
developers, who are too close to the design.

~~~
humanrebar
> The type/generic system picks up where C++ left off, and is very complex.

That's not entirely true. There are some mechanisms that you can't really do
in C++, at least not without a lot of boilerplate. And Rust's generics aren't
quite as powerful (as in metaprogramming) as C++'s yet. I hear they're working
on it, though.

~~~
jfoutz
I know they're working on higher kinded types, i think there's some fancy
template-template syntax you can use in c++ to get the same effect.

It's easy to define a list of integers in pretty much any language,

    
    
        class IntList { void addInt(){...} }
    

Most languages let you make a generic List,

    
    
        class List<T> { void add(T){...} }
    

The thing that's cool about HKT, is it lets you swap the other side,

    
    
        class T<Integer> { T operate(Integer) }
    

So you can deal with a Set or a List or an Optional or whatever might want to
deliver integers. It seems a little goofy, but it's sort of like a Super
Lambda. an anonymous function can only really do its one thing, but this lets
you bundle a bunch of related lambdas together, so if you have, say Pay, you
can group together all the different types of tax that are applied to pay,
calculate total tax, etc. And it works over whatever random data structure you
want

It seems a little crazy at first, but with clean syntax, it's a fantastic way
to split the work apart from the way the data is represented.

------
shadowmint

        The std::thread::scoped function used here is undergoing some redesign...
    

/grumble /grumble

Yes, I know, it'll all eventually settle down, but darn its annoying at the
moment trying to use stable.

~~~
steveklabnik
It's actually gone entirely now, so using nightly doesn't help you there. And
80% of Crates.io runs on stable, it really depends on exactly what you're
doing.

(The 'crossbeam' and 'scoped_threadpool' crates have different implementations
of this idea, though.)

~~~
slimsag
> 80% of Crates.io runs on stable, it really depends on exactly what you're
> doing.

While this is probably factually true -- I've had quite the opposite
experience. :(

\- I want to write benchmarks for my code, but I need `#[feature(test)]` AKA
nightly.

\- I want to [format my Rust
code]([https://github.com/nrc/rustfmt](https://github.com/nrc/rustfmt)), but I
need nightly.

\- I want to use Huon Wilson's SIMD crate, which requires nightly.

I'm not saying these things should be stable right now, but I am saying if you
try to use Rust right now you _will_ encounter a lot of stuff marked as
unstable and/or only usable on nightly due to XYZ. I still think Rust is
amazing and everyone should give it a shot though, but it's not as
stable/polished experience as I think it will be in say a year from now. The
language is stable, but the ecosystem just isn't yet.

P.S. You've helped me personally all too much over the Rust IRC channel, so
thank you a million times for that! :)

~~~
steveklabnik
You're welcome :)

I also think that year-from-now Rust will be way nicer than at the moment,
it's absolutely early days. But there's a lot you can do, even on stable,
right now. This is why I said it depends on what you're doing. Some use cases
have absolutely no choice but to use unstable things. Others have options, and
others only use stable.

As to your three points:

1\. You can configure things so that you only need nightly when doing
benchmarking, and run on stable the rest of the time.

2\. Compiling `rustfmt` needs nightly, but using it doesn't, as it's a binary.

3\. Yeah, the SIMD stuff just happened, so it's gonna take some time.

~~~
libria
It would be good if the core Rust team owned `rustfmt` the way the Go team
owns their extremely opinionated format tool. It _heavily_ enforces very
consistent code formatting throughout all publicly available Go code.

Developers like to say they want choice when it comes to formatting, but take
it away and you're left with resigned programmers who are very productive at
reading code.

^^ Perhaps not the right outlet for this, but maybe you can point me to where
this discussion is taking place.

~~~
steveklabnik
Yes, our intent is to officially adopt rustfmt, and we expect that it will be
widly used. It's just not ready yet. nrc, who's leading up the work, is a
Mozilla employee, and in general, everyone wants it to be ready. Software just
takes time :)

------
oneJob
I know this not the optimal place for this comment/question, but I think
likely that many who are interested in this post would be able to speak to the
post.

I'm also a huge fan of the Chapel Programming language. Anyone else think
they're both hitting a sweet spot and would potentially have a beautiful love
child?

------
tmaly
Is there a Tour of Rust like there is a Tour of Go? I found the Tour of Go
very helpful when I first approached the language.

~~~
Maken
There is the Rust book [https://doc.rust-
lang.org/stable/book/](https://doc.rust-lang.org/stable/book/), which explains
the basics of the language and the standard library though code snippets, and
Rust by Example [http://rustbyexample.com](http://rustbyexample.com), which
goes less in depth but is more interactive.

------
eklavya
Awesome read, really helpful. Thanks :)

------
0xe2-0x9a-0x9b
There is nothing in Rust that would ease solving hard problems. Graphs are
unsupported. No transactional memory. No distributed computing. No migration.
No support for trying. No memoization. No proofs.

~~~
btown
One might argue that "guaranteeing your web browser doesn't have memory leaks"
is a "hard problem." Ergo
[https://github.com/servo/servo](https://github.com/servo/servo) . I agree,
though, that the problems you mention are all ones where I'd love to see a
greenfield programming language with as much attention to detail and support
as Rust has.

~~~
steveklabnik
(Rust doesn't guarantee an absence of leaks, actually. Just memory safety.)

~~~
openasocket
In what conditions could Rust code leak memory, excluding the use of unsafe
code? What are the technical reasons you can't guarantee an absence of leaks?

~~~
steveklabnik

      >  What are the technical reasons you can't guarantee an absence of leaks?
    

Well, 'leak' is one of those things that's easy for a programmer to
understand, but a hard thing for a computer to understand, because it's really
about intent. How long did you intend for some resource to live? Any global
value is, in some sense, a leak. We had a long discussion about this, and, at
least currently, we couldn't come up with a formal enough definition of 'leak'
to even start tackling the problem of "how do we solve leaks." (It is entirely
possible that I am unaware about research on this topic... but given that
solving leaks wasn't a goal of Rust, fixing it would just be gravy anyway. You
have to choose your battles, and Rust certainly isn't perfect.)

As for how safe Rust can leak:

    
    
        let x = Box::new(5);
        std::mem::forget(x);
    

which can itself just be implemented in safe code:

    
    
        use std::cell::RefCell;
        use std::rc::Rc;
    
        fn forget<T>(val: T) {
            struct Foo<T>(T, RefCell<Option<Rc<Foo<T>>>>);
            let x = Rc::new(Foo(val, RefCell::new(None)));
            *x.1.borrow_mut() = Some(x.clone());
        }
    

or something like "I have a thread that is holding the receiving end of a
channel that infinitely loops without reading anything off," in which case
anything sent down that channel leaks.

~~~
btown
That's a very intriguing block of code there. I'd like to think that while
Rust may not have been designed to guarantee that leaks are prevented, it so
happens that anything that _could_ leak memory sticks out like a sore thumb in
code review, since the code to get something to leak needs to be explicit. I'd
consider a global variable or a channel that has the possibility of not
reading all its values to follow that pattern. Whereas it's much easier to get
something "stuck" in memory without a reference in C++. But you're absolutely
right that memory safety does not at all imply "leak" safety, nor is the
latter well defined at all!

------
0xdeadbeefbabe
> It’s very difficult to write multithreaded code, which is the only way to
> exploit the abilities of modern machines.

QED? Really? I'm not so sure I trust the author to give rust fair treatment
anymore. An operating system that does multithreading is not the same as a
modern machine.

Edit: Any brave down voters want to explain why? Threads are a way to model
concurrency. There are other ways.

~~~
JoshTriplett
More importantly, Rust goes out of its way to make it _easy_ and _safe_ to
write multithreaded code...

~~~
acconsta
Does Rust do anything to prevent deadlocks?

~~~
kibwen
Eliminating deadlocks in general isn't possible in a Turing-complete language.
What's cool about Rust is that it does statically eliminate a certain subset
of race conditions known as data races. So while deadlocks are still possible
in Rust, the typesystem is guaranteed to prevent concurrency errors that would
result in corrupted data.

~~~
acconsta
The Rust community makes a car:

"Check out our awesome new car! It makes driving ' _easy_ and _safe_ '!"

"Does it prevent crashes?"

"No, that's impossible! But here, look at our onboard computer that prevents
many types of driving errors."

I _like_ Rust. I want it to succeed. But I think the rhetoric gets ahead of
the language sometimes, and not prioritizing higher level concurrency tools
because you have the borrow checker is a mistake.

~~~
kibwen
It's incorrect to say that Rust hasn't prioritized higher-level concurrency
tools. Many of the design decisions over the previous years have been made
with an eye towards supporting every concurrency paradigm.

For example, here's a post of Niko Matsakis' from Feb 2014 about changing a
fundamental aspect of the borrow checker in order to better support data
parallelism in the future:
[http://smallcultfollowing.com/babysteps/blog/2014/02/25/rust...](http://smallcultfollowing.com/babysteps/blog/2014/02/25/rust-
rfc-stronger-guarantees-for-mutable-borrows/)

For another example, here's an RFC from Nov 2014 about fundamentally altering
Rust's concurrency support in order to better support fork-join parallelism in
the future: [https://github.com/rust-
lang/rfcs/blob/master/text/0458-send...](https://github.com/rust-
lang/rfcs/blob/master/text/0458-send-improvements.md)

I'm also confused about your perception that the rhetoric gets ahead of the
language. The type system does indeed make things easier and safer. When
people ask what that means, we're eager to elaborate on the precise guarantees
that Rust provides. Misleading people as to Rust's capabilities is not on the
agenda.

~~~
acconsta
>It's incorrect to say that Rust hasn't prioritized higher-level concurrency
tools

I think it's a fair characterization given 1.0 shipped without them, and there
doesn't seem to any timeline for standardization (please correct me if I'm
wrong).

I don't think I have to tell you that concurrency is one of the most important
challenges in modern programming. But all Rust gives you today (and for the
foreseeable future) is a pthreads wrapper.

>I'm also confused about your perception that the rhetoric gets ahead of the
language

The parent comment says verbatim "Rust goes out of its way to make it easy and
safe to write multithreaded code". This apparently doesn't include preventing
deadlocks, a problem that is common, hard-to-avoid, and difficult to recover
from. Does that not strike you as a bit of an overreach?

~~~
the_why_of_y
> deadlocks, a problem that is common, hard-to-avoid, and difficult to recover
> from

Deadlocks are by far the easiest concurrency problem to debug, since it's
_very_ obvious when your program is deadlocked, and in most cases a stack
trace of the involved threads is sufficient to debug and fix a deadlock. Also,
if your program is deadlocked it won't corrupt user data.

Rust's std::sync::Mutex is fortunately non-recursive, which makes it easier to
find deadlocks during testing.

Data races are far worse since they may cause arbitrary effects at a later
point in program execution, so they take a lot of developer time to track down
and very likely lead to data corruption.

~~~
acconsta
If deadlocks are so easy to fix _why do they happen so often_? Seriously, how
many applications have you seen hang in your life? (not to discount the role
of other race conditions in causing hangs, which the borrow checker also can't
categorically prevent).

~~~
GFK_of_xmaspast
Null pointer derefs are easy to find and fix and : yet.

~~~
neandrake
This is not always the case. Null pointer derefs are just indicators that the
program got into an invalid state. Is the fix to provide some alternative
pathway when the program got in a bad state or is it to prevent the bad state
from happening in the first place? If the fix is to prevent the bad state,
tracking down how that state occurred in the first place can often be time
consuming in the case of data races.

