
Rust, an Anti-Sloppy Programming Language - arthurtw
http://arthurtw.github.io/2014/12/21/rust-anti-sloppy-programming-language.html
======
aethertap
I've been writing a parser generator in rust as a way to learn the language,
and it's been a mixed journey of being very impressed, and very frustrated. A
large part of my frustration comes from what seems like a negative feedback
between algebraic data types and the borrow checker. I often find myself with
a piece of code that looks clean and simple, using the Option and Result
monads to deal with errors in an elegant way, but then I can't compile it
because of issues with borrowing. Ultimately, I usually have to break apart
the nice structure that feels natural and reimplement it as either a sequence
of match or "if x.is_ok()" statements, which just feels... wrong somehow. It
becomes tempting to just punt on the error handling and call unwrap() on
everything just to get it working for now, which leads to problems later on.
It's _so close_ to being incredibly expressive _and_ great for systems
programming, but it isn't quite all the way there (at least for me, at this
point).

I think that higher-kinded types will probably help a lot with this if they
make it into the language, and it's likely that there's a way to get around
the errors and get the full benefit from the algebraic data types and monadic
functions that I just haven't found yet. The language is enough of a level-up
from my C++ days that I'm sure I'll stick to it, even if those things aren't
true.

I thought this quote from the article really nailed it regarding my recent
experience:

"Expressiveness or elegance is not a goal of Rust. It’s certainly not bad in
this regard, just not as wonderful as you may wish if you care it a lot."

~~~
yazaddaruvala
Definitely one of the things that frustrates me about Rust, is that I feel
like I can't write "pretty" code. Multiple times it was even the reason I gave
up learning Rust. Clarification: I think my definition of pretty has come from
a certain feel/comfort you have when reading Ruby code (which I have
written/read for only a couple months).

Meanwhile, to continue learning Rust, I've done myself a favor. I just told
myself: "Rust is worth writing but it isn't for pretty code. Sorry!".

The other, related, mental block I haven't been able to overcome yet: In
Java/Python/Ruby/(even C++), the runtime "seems so heavy" I don't care when I
"waste" allocations or memory. However, the fact that it says on Rust's home
page "featuring zero cost abstractions" makes me feel guilty/incompetent every
time I introduce a cost-full abstraction. Again a reason I stopped learning
Rust a few times.

Sadly this feeling of inadequacy/guilt extends even to applications. Which is
ridiculous since if your application isn't doing expensive things, you
probably have a very simple application. This definitely plays a part in why I
can't write code as elegant as Rust will allow it.

For the record, sorry, I don't usually like critiquing something I don't have
a solution for. However, I thought this was worth bringing up. Mostly I needed
to vent, but does anyone else feel similarly? Am I really the only one?

If other people are feeling the same way, I hope it doesn't become a habit for
us to feel the only way to write good Rust is to allow it to be inelegant. It
would be disheartening if the next systems programming language was memory
safe but still noone enjoyed reading it. I'm curious what peoples thoughts
are?

Mostly joking, but maybe just a line on the website or guide saying: "We take
care of the performance so you can write expensive applications"?

~~~
ufo
Very interesting point and I also feel similar a need to "overoptimize" my
code when I program something in C. This is specially notable when working
with strings: In high level languages I use strings concatenation and
splitting willy nilly but in C all those strcats and for loops "feel"
suboptimal so I have a tendency to write hairy code full of pointers and
strtok.

~~~
toolz
Developers are a strange bunch. I've never heard someone say, "Wow, look at
that optimized code! I can't wait to work with this dude!". However, I have
heard many times, "Working with this person is great. They do everything from
expressive code, to small logical commits to make my life easier."

It's almost as if many of us would rather be seen as an elite, rather than
seen as a pragmatic developer.

~~~
XorNot
I usually just tell myself "bad code gets run". Optimizing string
concatenations and the like is something I can do _after_ the code is done,
doesn't leak, and has enough form that I can write function tests around it.

Its not something to do before.

------
alfiedotwtf
I haven't touched C or C++ in over 10 years as I've been living like a free
spirit in the Perl world. I've always wanted to go closer to the metal, but
Perl did everything I ever wanted. But I slowly started to feel that dynamic
languages had kind of boxed me in, almost making me timid of memory management
and handling resource allocation.

So I wanted to go deeper. I played with Go for a while, but kept an eye on
Rust. However over the past couple of months, Rust just felt like they got it
right. It made me care free about resources just like how Perl took care of
everything, while being a strongly typed, static language with generics, and
without the problems that might get in my way that Go has (GC and a heavy
runtime).

If you still haven't had a look but are interested, take a look at the Rust
Guide:

[http://doc.rust-lang.org/guide.html](http://doc.rust-lang.org/guide.html)

~~~
geofft
"Rust" was an interesting but slightly confusing language a year-ish ago, when
it had chans and tasks and libuv and M:N threading and three kinds of pointers
with their own funny symbol. That language, as far as I can tell, basically
stopped existing (for lots of small good reasons that added up). Rust 1.0 will
be an interesting head-on competitor to C/C++ without the awfulness of C/C++,
which seems like a very different thing, and a thing I personally have more of
a use case for.

I think, to some extent, this is why Rust keeps being compared with Go. Last
year's "Rust" was definitely something that merited comparisons with
goroutines and the like. Rust 1.0 is going out of its way to make sure that it
can be used for writing dynamic libraries that can be called from C or any
other language with an FFI, and is possibly easier than C++ for that use case.
This is completely not doable in Go (I suspect that this goal is fundamentally
incompatible with having goroutines or a similar built-in concurrency story).
Go seems to be a good language, but it's addressing a very different use case
from what Rust is (now) addressing.

~~~
mietek
_> I suspect that this goal is fundamentally incompatible with having
goroutines or a similar built-in concurrency story_

Not really. Here’s an example of writing shared libraries for C in Haskell:

[https://github.com/mietek/haskell-so-
example](https://github.com/mietek/haskell-so-example)

~~~
nostrademons
The "called from C" case isn't the interesting one, even though it's what the
grandparent mentioned. It's the "called from a scripting language" case. You
can call Haskell or Ocaml or even Java code from within CPython or Matz Ruby.
However, if you do this, you bring along an entirely different runtime system
and memory layout. Usually this means that you face some very odd memory
management bugs and lose any speed advantage you gain as soon as you have to
marshal objects across the language boundary.

~~~
mietek
Interesting opinion. What are the applications?

~~~
steveklabnik
One of the first production deployments of Rust is such an application:
[https://www.skylight.io/](https://www.skylight.io/)

Their Ruby gem is actually written in Rust.

------
Animats
A big problem with Rust right now is that the documentation is awful. This has
two implications. The less important one is that the language is harder to
learn. The more important one is that there's no architectural document that
prevents Rust from becoming a collection of features in search of an
architecture. If you have to clearly explain in writing how something works,
and the explanation is overly complex, that's an indication that simplifying
the design may be necessary.

The basic concept of ownership in Rust is simple. Everything has either a
single owner, or a reference counted cell as owner. Things which cross thread
boundaries have a locked reference counted cell as owner. Temporary references
to single-owner objects are allowed, but must have a shorter lifetime than the
primary ownership, and this must be demonstrated by simple static analysis.
That's straightforward.

Making those restrictions usable seems to require many features and much churn
within the language. It's too soon to tell how this will come out. As I'm
mentioned before, I'm bothered by the need for "unsafe" code in pure Rust
that's not calling hardware or C code.

~~~
steveklabnik
> A big problem with Rust right now is that the documentation is awful.

Could you get more specific for me, please? There's certainly much room for
improvement, but I can't fix what I don't know about!

> If you have to clearly explain in writing how something works,

Are you aware of our RFC process? Changes _do_ have to have extensive, written
explanations.

~~~
mnemonik
FWIW, I've found the Guide incredibly helpful, and in general found the docs
very satisfying. (Although I do wish the pointers guide was more complete :) )

The API docs + search in the docs are incredible IMO. I'm in absolute love
with rustdoc. Having links to the source right there is the most valuable
thing ever, both when trying to understand how to use a given API, and when
trying to write similar things its really useful to be able to look at how
others have done things.

~~~
steveklabnik
Awesome. I'll consider bumping up the Pointer Guide in my priorities.

------
krilnon
The author's term "anti-sloppy programming" immediately brought to mind a
former colleague's paper, aptly-titled "Sloppy Programming" [1].
Interestingly, Rust is a programming language, while this paper describes an
editor/IDE enhancement to turn natural language style input into ASTs.

This suggests to me that the most anti-sloppy languages like Rust would
benefit the most from sloppy input inference techniques. Has anyone been
working on developer tools to make writing Rust code easier? (Lifetime
management is the obvious new language feature to target.)

[1] [http://groups.csail.mit.edu/uid/other-pubs/sloppy-
programmin...](http://groups.csail.mit.edu/uid/other-pubs/sloppy-
programming.pdf) (pdf), or less-detailed:
[http://groups.csail.mit.edu/uid/projects/keyword-
commands/in...](http://groups.csail.mit.edu/uid/projects/keyword-
commands/index.html)

~~~
portmanteaufu
> (Lifetime management is the obvious new language feature to target.)

If you haven't toyed with Rust since late summer when Lifetime elision[1]
landed, it's very worth revisiting. That feature made writing out lifetime
notations unnecessary in 87% of the standard library. It's made things a lot
more pleasant. There's still plenty of room for IDEs and editors to help out,
of course, but the language itself is making great strides in usability as it
matures.

[1] [https://github.com/rust-
lang/rfcs/blob/704f0060176418659698e...](https://github.com/rust-
lang/rfcs/blob/704f0060176418659698eb63642e2071b109e029/active/0000-lifetime-
elision.md)

~~~
krilnon
Ah, great; thanks for pointing out that it has changed. The last time I looked
at Rust in detail was mid-April, so I hadn't seen that.

~~~
wycats
As someone who wrote a lot of code _not_ in the standard library, I championed
this RFC pretty heavily. Part of the reason is that I noticed that with the
lifetime elision rules, code _using_ abstractions (as opposed to the
implementation of abstractions) tends to use even fewer lifetime annotations.

Incidentally, the 87% number is a bit misleading. Before the Lifetime Elision
RFC, a limited kind of elision was already allowed (when a function borrowed a
value but didn't return a borrowed value). The 87% number was the number of
lifetime annotations that were still required even with that limited rule that
could be removed with the new rule. When taking all kinds of elision into
consideration, the number _in the standard library_ is closer to 95%. (many of
the remaining cases in the standard library involve the implementation of
collections)

In practice, that means that the vast, vast majority of borrowed references
don't require explicit lifetime annotations, and that is pretty close to 100%
in "application" code built on top of abstractions.

------
Hypx
Since rust is a true systems programming language, without a mandatory GC, I
wonder if there will be any serious attempt to build a kernel entirely in
Rust? It certainly sounds possible, and I suspect it would quickly be better
than any FOSS kernel currently available, given the fact that Rust is so well
designed against bad or sloppy code. Perhaps I'm just talking out of my ass
here, but it sounds like a good idea.

~~~
imanaccount247
There's already a high performance, formally verified, "FLOSS" microkernel:
[http://sel4.systems/](http://sel4.systems/)

As it turns out, very few people are interested in having software that works.

~~~
coldtea
I call BS.

The reason very few people care is that "works" is not a binary, but obviously
a continium -- and people inherently understand diminishing returns...

Especially when the "works" they care about is getting shit done, by having
access to software (including proprietary), drivers, a community, vendors,
etc.

The "is formally verified" they could not care less, and rightly so, if their
other demands are not satisfied...

~~~
Animats
That L4 kernel is in the air link CPU of most cell phones.

~~~
coldtea
Nice that it found a niche there.

That's however orthogonal to what I wrote, I think.

It's not like it has a software ecosystem, community, drivers, etc, to be
adopted over any major OSes as something that "all others being equal" is also
"formally verified".

------
pdq
The quickest comparison I'd say between choosing Rust and Go is:

\- if you like C or Python, you will probably prefer Go

\- if you like C++ with templates or D or Ruby (especially Rails), you will
probably prefer Rust

We will see how the languages evolve, but so far I really enjoy Go having
native channels, having a stable language definition, and a simple yet stable
and powerful standard language API. Also the public/private access system
based on capitalization is genius, and the package management is very
intuitive.

~~~
bad_user
If you "like C", then Go is a really poor substitute, because Go doesn't allow
one to juggle with memory like what people are normally doing in C. Rust is
not comparable with Go, because Go relies on a non-optional GC, a difference
that changes everything, Go being in the same league as the JVM, except that
the JVM is better at almost everything, like garbage collectors, tools and
libraries available, multiple languages etc. The JVM is even better at
concurrency.

Go in my opinion is the best proof that our industry is fashion driven.

~~~
wtf_is_up
>Go in my opinion is the best proof that our industry is fashion driven.

It's not though. What is fashionable about Go? Most testimonies I've seen
state solid reasons for choosing it. Nobody claims it to be a marvelous
language like Haskell. It's a simple language that's easy to pick up,
implement an idea, and deploy. Much like python, but with much greater
performance.

I don't see what fashion has to do with it.

~~~
lmm
OCaml offered the same proposition for 10+ years. No-one was interested. Oddly
enough, now that Go and Rust are getting attention, people seem to also be
taking a second look at OCaml. What's that, if not fashion?

~~~
jerf
"OCaml offered the same proposition for 10+ years."

No, it didn't. In addition to the already-observed point that Go's concurrency
story has always been better than OCaml's, Go also offers it to you in an
Algol-esque syntax. It isn't C, but if you know C the differences are
dialectal, whereas OCaml is from the ML tradition and is radically different.
ML is the language family probably most distant from C in current medium-scale
usage.

I don't like or celebrate this fact, but it seems that broad-scale success
right now _requires_ that you be an Algol-descended syntax.

~~~
the_why_of_y
Allow me to summarize your post as "OCaml syntax is not and never was
fashionable", thus proving the OP's point :)

------
dbaupp
One thing the dynamic language (and Go, to some extent) comparison misses is
speed: in general, Rust code is likely to be faster to execute than the
equivalent Perl/Python/Ruby/Go, due to performance being a strong design
choice of the language, and the use of the industrial strength LLVM optimiser.

This is a general statement, it's not always true; but the control Rust
provides theoretically means one can always wrangle the Rust code to be equal
to/faster than most other languages.

(In fact, the embeddability of Rust means it is pretty well suited to writing
the parts of a Python/etc application that need performance, since Rust can
easily make dynamic libraries which can be loaded as extension modules.)

------
EdiX
Rust is probably the best programming language I will never use. Most of the
chances I get to experiment with new languages is in small and/or personal
projects. None of those projects ever benefit from manual memory management so
the main selling point of Rust (safe memory management without GC) is actually
just a hindrance.

~~~
kbart
I'm sure Rust will be adapted to various boards for enthusiasts as soon as
stable version appears and it will be suitable for small personal projects.
There even have been some tries[1] and Zinc still looks pretty active[2].

1\.
[https://news.ycombinator.com/item?id=8035293](https://news.ycombinator.com/item?id=8035293)
2\. [https://github.com/hackndev/zinc/graphs/commit-
activity](https://github.com/hackndev/zinc/graphs/commit-activity)

------
maugzoide
Good post. I haven't coded C++ for like 3 years so Rust is a very new concept
to me (I am a Python programmer). This weekend I spent some hours to build a
hangman game:
[https://github.com/mauricioabreu/hangman](https://github.com/mauricioabreu/hangman)

------
cobookman
I've been wanting to mess with Rust for webapps. But I've been wondering on
how it performs with a lot of async operations. Is the idea that you write CPU
heavy code in Rust then call the compiled blob in say a node.js app, or is the
end goal to one day compete with node.js?

~~~
kibwen
Async I/O is still an open question in Rust. Over the next year I expect
third-party libraries to experiment heavily in this space, which will
hopefully blaze a trail for a blessed solution to be uplifted into the stdlib
at some point (or if not a complete solution, at least the infrastructure to
make it reasonable).

------
markbao
Did anyone else find Rust's package/module management kind of, well, sloppy?
Perhaps coming from Rubygems and `go get` has made me spoiled. (And npm, and
Cocoapods, and pip...)

~~~
dbaupp
Have you used cargo and crates.io? It is effectively the systems-language
successor of Ruby's bundler (designed and (initially) built by the same
people), but also paying attention to lessons learnt in npm and Go etc.

~~~
markbao
Yes, that's what I used—however, I just realized I may have been conflating
the difficulty of doing package management in Rust with some packages that I
used that were enormously difficult to use or were outdated (speaks to the
pre-1.0 thing, I suppose).

------
musername
Since the ownership system and borrow checker are tuted as new, wouldn't a few
papers been appropriate? Sure would be fit to document development outside of
the community.

~~~
steveklabnik
[https://github.com/rust-lang/rust/wiki/Note-
research](https://github.com/rust-lang/rust/wiki/Note-research)

(There is also at least one recent paper on Rust which is t linked here.)

------
marianov
Does Rust have anything like Python generators or coroutines? I don't even
know if its possible on a strongly typed laguage, but it's a killer feature
form me.

~~~
wycats
I would be very surprised if something like C# and ES7's `async/await` syntax
doesn't land in Rust after 1.0.

~~~
jpgvm
It can be difficult to implement well on _nix systems. async /await works so
well in C# because IOCP in Windows is completion based. I/O on current _nix
OSes is readiness based (epool/kqueue).

I implemented a completion based wrapper ontop of libev and libaio in C but
could never quite nail it.

It's worth mentioning that IOCP can be used on any fd in Windows, whereas
Linux epoll loop can only be used for sockets. There is a caveat to that
however, you can use the epoll loop if you use eventfd with some effectively
undocumented behavior of the aio subsystem that allows you to register an
eventfd with your aio context. There are drawbacks to this though, you have to
use O_DIRECT and aligned writes. In Windows you can use IOCP to do async
buffered writes...

Thankfully much smarter people work on Rust and hopefully they manage to make
a completion based IO system that is portable across the operating systems.

~~~
ufo
I don't see why the IO model would be a fundamental issue here. Fundamentally,
async/await is about automating inversion of control, which is a mechanical
program transformation.

~~~
jpgvm
Unfortunately most of what you want to do with await/async is IO based, the IO
model matters a lot if you want it to be scalable. Without kernel support
trying to build a completion based API is not very performant. Trying to
emulate it in a library proved painful, especially when trying to do IO on
files.

~~~
thristian
I'm not sure that's true. The same kind of mechanical transformation that
gives await/async also leads to Python-style generator functions, and I wind
up using them all the time for all kinds of iteration tasks, not just I/O (or
at least, not just async I/O).

~~~
barrkel
You're conflating two distinct concepts.

Certainly, code rewriting to support continuation is a factor in both C#-style
await and generator functions, but they are not the same thing.

Much of the motivation behind async I/O is to reduce the number of active
threads blocked on I/O. Blocked threads means stack space consumed, kernel
transitions, and potentially CPU contention if too many threads become
runnable at once, and then start fighting over mutexes or cache.

In this way of looking at the world, you don't want to _block_ for any I/O.
That's what the GP is getting at.

------
amelius
Can anybody explain in simple terms how Rust can manage cycled memory, without
a garbage collector?

~~~
kzrdude
You'll have to work hard to manage to create a cycle in the first place,
regular ownership through a value or a Box<T> will not allow it. You can
create cycles (that need to be broken manually) with the reference counted
smart pointer Rc<T>, or you use its weak pointers to resolve it automatically.

Oh, and datastructures that need cycles (the libstd's doubly linked list, for
example), can use raw pointers to manage it manually, encapsulated in the
implementation.

------
abiox
i just want to know if i can call a web json api with the stdlib :(

~~~
steveklabnik
HTTP client / server is out of scope for the stdlib. Just add one line to your
Cargo.toml and you're good to go, though.

------
jokoon
any valuable starting tutorial for c/c++ programmers ?

~~~
Animats
[https://github.com/rust-lang/rust/wiki/Rust-for-CXX-
programm...](https://github.com/rust-lang/rust/wiki/Rust-for-CXX-programmers)

There's also [http://featherweightmusings.blogspot.com/2014/04/rust-
for-c-...](http://featherweightmusings.blogspot.com/2014/04/rust-for-c-
programmers-part-1-hello.html) which is longer, but not as useful.

Rust has roughly the feature set and memory model of C++. The difference is
that in Rust, the safety issues C++ sweeps under the rug are addressed. As I
point out occasionally, the three big problems in C/C++ are "who owns it",
"how big it it", and "who locks it". C fails to deal with any of those issues.
C++ sometimes deals with "how big is it". Rust deals with all of them.

In Rust you have to deal with most of the memory management headaches you have
in C++, but they're detected at compile time, not after the product ships.

~~~
tonfa
How likely is it that a future version of c++ would tackle this issues?

~~~
steveklabnik
It's impossible to address 100% in C++ without breaking backwards
compatibility. "Modern C++" is significantly more safe, but still isn't
completely safe.

