
Four years with Rust - steveklabnik
http://words.steveklabnik.com/four-years-with-rust
======
Animats
Rust as a language is now realizing the benefits of borrow checking. As the
article points out, the syntax doesn't have to distinguish between move and
assign. The borrow checker will catch a reuse of something already moved away.
This turns out to be effective enough in practice that the syntax distinction
isn't necessary. That wasn't obvious up front.

Not having exceptions tends to generate workarounds which are uglier than
having exceptions. Rust seems to be digging itself out of that hole
successfully. Early error handling required extremely verbose code. The
"try!()" thing was a hack, because a expression-valued macro that sometimes
does an invisible external return is kind of strange. Any macro can
potentially return, which is troublesome. The "?" operator looks cleaner; it's
known to affect control flow, and it's part of the language, so you know it
does that. Once "?" is in, it's probably bad form for an expression-valued
macro to do a return.

There's still too much that has to be done with unsafe code. But the unsafe
situations are starting to form patterns. Two known unsafe patterns are
backpointers and partially initialized arrays. (The latter comes up with
collections that can grow.) Those are situations where there's an invariant,
and the invariant is momentarily broken, then restored. There's no way to talk
about that in the language. Maybe there should be. More study of what really
needs to be unsafe is needed.

~~~
elihu
> There's still too much that has to be done with unsafe code. But the unsafe
> situations are starting to form patterns.

I think as long as those patterns can be abstracted out and moved into
thoroughly-vetted libraries with a safe interface, unsafe code isn't really a
problem. I expect that getting Rust's standard libraries to a place where
regular applications very rarely need to create their own unsafe code blocks
is going to be a major long-term effort.

(Of course, if there were some simple language feature that would make some
common use case of unsafe code blocks unnecessary, by all means we should do
that as well.)

~~~
paulddraper
Agreed. And considering that the most common uses of unsafe are for
collections, I think a limited number of vetted libraries completely
realistic.

Rolling your own collections is typically not that great anyway; how much
better is your linked list/hash table/skip list than everyone elses?

~~~
phamilton
As long as there are 40+ collections data structures like in Java.

------
mr_luc
For those of us who are getting to the party 3 years late, thank you.

Just my 2p for others learning: for me, Rc::RefCell was what I was missing,
even after I thought I was up to speed. I was fine using Channels for inter-
thread communication and I never needed Arc, but use of Rc is common in the
Rust ecosystem and a lot of my early fights with the borrow checker weren't
fights I needed to have. In situations where it wasn't a trivial change, and I
found myself banging my head against the wall, it was often because I was in a
situation that called for a reference counted cell.

~~~
amelius
Reference counting can be a useful memory management technique. But reference
counting may cause unpredictable pauses when large amounts of objects suddenly
need to be freed (e.g. when dropping the last reference to a large array
holding many references).

At least a carefully written garbage collector can free objects incrementally,
and concurrently.

So memory management in Rust is certainly not a solved problem.

EDIT: Removed mention of RefCell.

~~~
cesarb
> But reference counting may cause unpredictable pauses when large amounts of
> objects suddenly need to be freed (e.g. when dropping the last reference to
> a large array holding many references).

That's still a predictable pause: it's the number of references contained
within the array. If you know your array will hold at most ten references, it
will take at most the time to free the ten references. Also, you know this
will happen when the last reference is dropped - no earlier, no later. If your
program can't handle the pause at that moment, you hold the reference until it
can. If you know another component (perhaps in your own call stack) holds a
reference, you know it won't be freed. And so on.

~~~
amelius
> If you know your array will hold at most ten references, it will take at
> most the time to free the ten references.

True. But if those references themselves contain references, it may become
tricky to manage all of this. You basically don't want to think about it.

But, like others mentioned, you _can_ put the objects in a queue, and free
them in the background. It would be interesting to know how such a solution
stacks up, performancewise, against an incremental, concurrent garbage
collector.

~~~
Manishearth
Note that you don't need a GC to do that; the allocator can do it for you.

A lot of folks think that optimizations like having allocation arenas and
spreading out deallocation pauses are unique to garbage collectors, but
they're completely orthogonal to GC. You can have GCs with these
optimizations, and GCs without. You can have regular allocators with these
optimizations, and regular allocators without. Jemalloc does have arenas and
stuff for allocation (I'm unsure if it spreads out deallocation loads). Of
course, with a GC you can also defer the cost of iterating through large
vectors and calling destructors.

But anyway, this only becomes a problem when you have large complicated Rc-
trees in your application, which tends to not be the case in Rust.

~~~
raphlinus
An exception (large complicated Rc-trees) is the rope in xi-editor. If you
load a very large file, the operation of letting go of the last reference is
potentially a large enough pause to have an effect on UI responsiveness. I've
considered adding a mechanism that moves the object into a deallocation thread
(or a deallocation task running in a thread pool) for this reason, although
I'm not sure how important it is in the grand scheme of things.

In a GC'ed language such as Go, this would not be a problem. The flip-side is
that I can safely and efficiently do updates in place, because Rust's
reference counted type has a way to determine when you're holding the only
reference.

~~~
MaulingMonkey
> I've considered adding a mechanism that moves the object into a deallocation
> thread (or a deallocation task running in a thread pool) for this reason,
> although I'm not sure how important it is in the grand scheme of things.

The one time I've done this in C++, delayed deallocation actually made major
loads/unloads worse for us on mass batch operations - more cache thrashing
perhaps? Since this involved GPU resources, perhaps some bad interaction with
the driver? I did keep around the "optimization" _conditionally_ for smaller
operations as this let us get rid of some hitching.

> In a GC'ed language such as Go, this would not be a problem.

In C# this traditionally manifested as lengthy GC pauses you had to jump
through hoops to workaround. Modern GCs are much better these days, but it's
not 100% solved.

~~~
xorxornop
Hmm, yeah cache thrashing sounds a likely culprit - agreed. Perhaps before
dropping all the references they should be sorted by memory address, so that
page faults are minimised.

------
losvedir
Wow, four years already. Maybe you can help settle this question I've had. I'm
a rubyist (like you were/are, and wycats, and a bunch of rustaceans), and I
think that sort of drove my interest in rust.

But after 4-5 years of ruby the dynamism which initially was super cool, has
grown a little frustrating and I long for a more sophisticated type system and
a compile step, since frustrating bugs crop up from time to time that would be
caught by that, and which slip through a hole in our test suite.

I've dabbled in Haskell and Rust and the whole "if it compiles it's likely to
work, or at least the bugs will be significant logic ones" aspect of them is
very cool.

I wonder, though, if the flipside is true? Maybe people who have a compiler
and strong type system get frustrated with its rigidity over a number of
years, and when they see ruby for the first time are blown away with what it
can accomplish. After all, it took a number of years day in and day out with
ruby to start seeing its blemishes, I wouldn't be surprised if the reverse
were true.

So as someone who's now spent 4 years in the other grass, do you still find it
greener? (Or am I wrong from the start, and maybe you like Rust for other
reasons and its type system wasn't something that attracted you to it over
ruby?)

~~~
prewett
Are you talking about strong typing or static typing? I've been using
statically typed languages for 20 years (C++, Java, Objective-C) and I haven't
gotten tired of it yet. Especially when working in a large code-base I didn't
write, Python is rough. What's a "session"? Who knows, guess I'd better put
`print type(session)` and figure out how to get that function executed.

Although, the thing that bothers me about Python (haven't used Ruby) is the
fact that it is interpreted. It's really annoying to have to run my program to
discover syntax errors and typos, when a compiled language would do that for
me. With interpreted and dynamically typed languages, you have to have 100%
test coverage, because you have no idea if your code is even syntactically
valid until you run it, and of course if it isn't valid, then you throw an
exception. (Hopefully that crashes the program, unless someone was brilliant
enough to catch all exceptions and not print any error messages, then it looks
like everything worked fine until you discover it didn't.) So every change is
a potential for an exception, until you execute that line of code to make sure
there wasn't a misspelled variable name or something.

So, yeah, I love the compiler. (I also love Python for scripty stuff.)

~~~
gedrap
> until you execute that line of code to make sure there wasn't a misspelled
> variable name or something

But isn't it the purpose of tools such as IDEs? PyCharm is pretty damn good at
it. Of course, it's not always possible or convenient firing up an IDE. But I
suppose editors such as vim should be fully capable of doing this via plugins
as well.

~~~
xorxornop
The best use case I know of is avoiding the common Python & Ruby pitfall of
"just stick everything in a dictionary and index it by key". Welp, forgot to
update every instance of that string index (particularly if that name overlaps
with anything else, including variable and function and class names... so,
like, a lot of the time).

------
moosingin3space
As a relatively new Rustacean (since May 2016), I have to say that Steve has
been one of the most influential people on my Rust coding style and
understanding of the importance of community in a language. Prior to talking
with Steve on IRC, I believed that community was secondary to language
quality, but now I fully understand the importance of strong community in a
programming language.

~~~
notheguyouthink
I've moved away from Rust lately (long story), but in my attempts to move to
Rust i was blown away by Steve. His presence in the Rust community is
staggering, and his efforts truly made a Rust newbie like myself feel very
welcome.

------
niftich
Many thanks to the Rust team and the Rust community!

Also, retrospectives and posts where a core contributor summarizes the
achievements and accomplishments of the previous few years are extremely
valuable. These days software and tools evolve really fast and old resources
fall off the face of the earth for a various reasons, and it's important to
understand how things used to be and how we got to where we are now.

~~~
swuecho
I learn something new about rust, eg. irrefutable patterns as function
argument in the blog.

PS: I do not understand what irrefutable patterns means, so I google.

[https://www.haskell.org/tutorial/patterns.html](https://www.haskell.org/tutorial/patterns.html)

in case anyone do not know yet.

------
danjoc
I really like the premise of Rust. Keep at it. For my purposes, it's still a
bit too immature. Last time I looked at the big 7 things, most were still not
done.

[https://mail.mozilla.org/pipermail/rust-
dev/2014-June/010139...](https://mail.mozilla.org/pipermail/rust-
dev/2014-June/010139.html)

The RustDT plugin for Eclipse makes the edit/save/compile/flag errors loop a
lot tighter for me as a beginner. The autocomplete seems incomplete though and
there's no hover docs or ctrl-click through to source that I remember.

I look forward to the day I can replace JS with Rust. It would be nice to have
a quick way to start doing Rust in the browser. I tried installing emscripten
to give it a go, but the version in Ubuntu's deb repo was too old. I don't
remember why, but building it from source didn't work out.

I also got a bit lost trying to get boilerplate together for that purpose.
Something like maven archetypes would be really great for Rust... Project
templates that get you started with a working hello world for whatever you're
trying to do.

Just some suggestions. I basically like where Rust is going.

~~~
steveklabnik
A quick summary of where these are at:

Internationalization / Localization / Unicode (ICU): yup, no real progress.
Needs some domain experts to drive it.

Date/Time : chrono is the most popular.

HTTP: hyper has been good for a few years now, tokio will make it async soon.

Crypto: there's lots of interesting work in this space, see _ring_ and rustls.

SQL: Diesel is the gold standard here.

So, some progress! You're absolutely right that growing the crates ecosystem
is vital; we've doubled our numbers in the last year, but there's always more
to do.

> project templates

[https://github.com/rust-lang/cargo/pull/3004](https://github.com/rust-
lang/cargo/pull/3004)

~~~
danjoc
Thanks for your reply Steve. You're a stand up guy. I'll look into some of
these over the holiday. I want to give the new rustup a whirl.

------
0xFFC
Steve is one of the amazing guys I have ever known. He answered all of my
questions in IRC, he is such nice guy. I truly like him a lot. and most
important point is Rust team chose him , because they realized how wonderful
he is in communicating with people and being leader of community.

~~~
bluejekyll
As far as I can tell he answers every question, and corrects every inaccuracy
across at least these platforms: twitter, hackernews, reddit,
[users|internals].rust-lang.org, irc; its mind blowing. Honestly, a talk on
how he stays so connected without being awake 24/7 would be interesting ;)

Thanks, for all the hard work, Steve. You're devotion helped turn me into
someone who loves Rust.

~~~
steveklabnik
Thanks both, and achanda358!

It's taken lots of work. I think I'm a nicer and better person in Rust than I
was in Ruby, but that's all I'll say about that. I am very much not a perfect
human being.

> Honestly, a talk on how he stays so connected without being awake 24/7 would
> be interesting ;)

I wrote a blog post on that a while back: [http://words.steveklabnik.com/how-
do-you-find-the-time](http://words.steveklabnik.com/how-do-you-find-the-time)
it's still mostly accurate kinda.

~~~
ev0l
This post is amazing! The best quote is ...

"For example, the biggest time I scrubbed out in my entire life was a choice
that I made almost a decade ago: going to college."

A refreshing insight from a very intelligent individual!

~~~
steveklabnik
I have more complex feels about it as a general thing, but for me, I'm not
sure I made the right call. I did learn a lot, and I made some very dear
friends, but it was extremely expensive. If you (or anyone reading) are
someone who's making this decision, please give it serious consideration, and
don't just copy me: this is a really, really big choice to make in your life,
and one you deserve to make for yourself.

------
achanda358
You have relentlessly pushed for better documentation. Thanks for that :)

------
thinkpad20
> We removed the repl from the tree, as it never really worked, and was a
> maintenance nightmare. This one is the only one I know of today. Some people
> ask about one, but nobody has done the work to get a good one together yet.
> It’s tough!

This is a bummer. I remember when I first tried out rust the repl was still a
thing. As someone who writes mostly Haskell and Python, interactivity with a
language is a huge plus. I hope that eventually another repl comes around.

------
sowhatquestion
I'm highly curious about Rust as a potential introduction to systems
programming. One thing that bothers me is the lack of proper classes. This
seems to have become a trend among newer languages, as though traditional OOP
is an outdated paradigm.

I have yet to be convinced that this is a good move. The attempted
justifications I've seen hand-wave about problems with OOP, "composition over
inheritance," etc. Meanwhile, in the real world, 99% of production code is OO
and the paradigm continues to do its basic job--helping programmers model
domain objects. Older languages like JavaScript and PHP have made their OO
constructs _more_ robust over time, which suggests to me that in the end a
critical mass of developers will always clamor for this easily understandable
and productivity-enhancing paradigm over some theoretically pure alternative.

~~~
pka
So what is it that you need classes for that dearly?

------
pjmlp
Congratulations on the work to make our computing infrastructure safer.

------
cpeterso
It's interesting to see just how much the Rust language and libraries have
evolved. Is there a wish list of breaking language changes waiting for a Rust
2.0 version?

~~~
steveklabnik
We do have [https://github.com/rust-
lang/rust/issues?q=is%3Aopen+is%3Ais...](https://github.com/rust-
lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3Arust-2-breakage-wishlist)

It is unclear if there ever will be a Rust 2.0. Even those that want it agree
that unless it's incredibly easy to upgrade to from Rust 1.x, it's a non-
starter.

~~~
Lev1a
There is also the possibility that a Rust 2.0 could fragment the community
much like with Python2.7 <-> Python3.x, which would be "undesirable" at best.

~~~
steveklabnik
Yes, a split like this would be undesirable to say the least. Not to mention
that systems people are used to near-total backwards compatibility; any sort
of near-term timeframe for such a thing would destroy a lot of our
credibility, in my personal opinion. I'm on team "never 2.0".

We still have some desire to indicate "epochs" of Rust development, as
undoubtedly, things like idioms will change over time, new libraries will
replace older ones, etc. But I'd prefer something like "modern C++", which
could signify this kind of change, without giving up on backwards
compatibility.

~~~
Manishearth
I sort of want to make an RfC for 2.0 preparation. I am mostly on team "never
2.0", but I recognize that I am sadly not overlord of all things Rust (if I
were we'd have stable emoji identifiers already) and it's quite possible that
2.0 will happen some day.

The idea is to come up with a set of processes for 2.0. If the community
decides to do a breaking 2.0 for some reason, we should:

\- Document _exactly_ what has changed.

\- Write extensive docs on upgrading

\- Write good tools that do the upgrade for you when possible, and point out
areas where they can't help with links to docs.

\- Make it so that the cases where stuff isn't machine-upgradeable are minimal

\- Be wary of actually removing deprecated APIs.

\- Be wary of unnecessary extra breakage.

Python 3 took the attitude of "Okay, we're going to be breaking some things
anyway, so let's break more!". I think that they had good reasons for doing
that; and it makes sense in a way. But we may not want to follow the same
philosophy.

~~~
CryZe
I feel like you could just add a version number to the Cargo.toml (cargo new
would automatically set the latest one). The crate would then be built with
the semantics of that version and all crates you use still use the version
they were designed for (as long as the semantics can be properly translated
between the individual versions).

~~~
Manishearth
That wouldn't work in practice. Breaking changes need not be syntactic, and
probably _wouldn 't_ be syntactic -- Rust isn't going to break the language
for that. There's a good chance it would be semantic breakage that can't allow
interoperation between crates.

~~~
comex
What sort of semantic breakage do you have in mind? My impression is that
there are few changes of that sort that couldn't be worked around somehow.

~~~
Manishearth
Changes to the orphan rules, for one.

Changing the behavior and representation of stdlib APIs. You would end up with
e.g. two incompatible representations of String being used across the crate
boundary.

------
namelezz
I would appreciate if anyone can share your dev setup for Rust. I tried Rust
and Racer long time ago and it's not a pleasant experience.

~~~
Manishearth
VS Code with RLS is amazing. Still a work-in-progress, but some stuff works
and it's only going to get better.

I'm mostly just used to sublime and vim with minimal tooling so it doesn't
matter as much for me. But I'll probably eventually write a plugin for RLS for
sublime.

~~~
pjmlp
> VS Code with RLS is amazing.

The only reason I have to install it.

------
Annatar
_In today’s Rust, this would be an error, you need to write (e.f)(); At least
the error tells you exactly what to do!_

If the programmer knows what the error is, and how to correct it, then their
program should automatically do so, rather than molesting the user, as the
purpose of computers is to speed up and automate. Why should the user have to
foot the designers' bills?

Another cardinal sin and a sign of lack of thinking things through is breaking
backward compatibility, like the example above: imagine you were an early Rust
adopter, and wrote a non-trivial application in Rust; now imagine that the
newest Rust compiler has several major performance and efficiency gains. It is
understandable why one would want to re-compile one's application with the
newest compiler, only to be thwarted by the authors' lack of understanding of
just how important not breaking users' applications is. That is one of the
core differences between _engineering_ and _hacking_. What other land mines
await potential Rust adopters from programmers who do not have any respect for
their users' time?

~~~
GolDDranks
It's not the job of the compiler to speed up and automate _editing_code_.
That's the job of an IDE. And we have a unfolding story for that: Rust
Language Server, or RLS for short, which is a package providing pluggable Rust
IDE-functionality for all kinds of text editors.

Besides, the change we are talking about happened 4 years ago, when Rust
emphatically did NOT have any compatibility guarantees and was in a process of
rapid experimentation. This has changed a whole lot – it's a stable language
now, and has been for a year and a half.

~~~
Annatar
_It 's not the job of the compiler to speed up and automate _editing_code_._

Calling changing the syntax on unsuspecting users __editing_code__ is really a
stretch of gargantuan proportions. And I don't really care whether it's the
compiler, the linker, or mega_mojo-2.546-alfa5-preview18, what ever it is, it
runs on a computer so that whatever is being done would be fast, automated,
and therefore efficient. If the programmer knew what to do with the input, the
computer should churn through it, instead of chastizing the user and making
them pay for the authors' oversight.

Otherwise, a computer makes no more sense than a toy does.

If I want to use a computer as a toy, I'll go play a video game. The rest of
the time, it's a tool, a tool to do something an order of magnitude faster
than a human could. If it slows me down because it requires me to _babysit it_
, I have a problem.

~~~
xorxornop
That's all well and good if the context is unambiguous, but if it isn't,
people will get used to having this done for them (or not even know that it
happens at all), and then be totally perplexed. Worse, maybe then people want
even ambiguous situations to be made friction-free, so the compiler must
guess. Then you've got the possibility of very confusing behaviour, doubly so
if the ambiguity involves any kind of polymorphism (different, possibly
contradictory behaviour per type!)

------
dispose13432
What would be interesting if it would be possible to develop with GC on, but
then if you benchmark and notice that it's too slow, turn on manual garbage
collection for specific pointers.

I don't know if it's possible, though (considering libraries).

~~~
kibwen
Rust doesn't have a GC in the first place, so there's nothing to turn on. Even
then, the fundamental hurdle with GC isn't the effect on your program's
runtime, it's the effect that it has on _the lifetimes of your data_. GC (and
RC) are means of dynamic lifetime determination. Manual memory management is
static lifetime determination. The latter requires you to structure your code
in a specific way, which may be less convenient for the programmer depending
on the application. Converting dynamic lifetime determination to static
lifetime determination then requires changing the very structure of the
program itself (including how APIs work) while preserving semantics, which is
beyond any existing tools.

~~~
dispose13432
I was just thinking that most languages are either GCed (like Java or Go) or
manually curated (C/C++/rust).

My idea is that if one could write in a language like Go (which has some kind
of GC), but when one wants, say "I'll take care of this memory".

~~~
theseoafs
Rust actually started with a model like this. The idea was when starting out
for the first time you can use garbage collected pointers for everything and
then switch to the manual model when you needed it. Problem is then that
unless your standard library and all the major open source libraries work
perfectly for both the garbage collected objects and for the manually managed
objects then you need to learn manual memory management early on anyway, so
the garbage collector wasn't helping anyone. The language design element of
this is really tough to do in a usable way.

D has optional GC but their standard library is kind of fucked up.

------
lacampbell
Getting rid of the syntactic difference between moving and copying seems like
a huge step backwards. Ditto with abandoning mailing lists for some wanky
"already solved-as-a-service" web app.

~~~
pcwalton
> Getting rid of the syntactic difference between moving and copying seems
> like a huge step backwards.

Did you ever use Rust back when you had to write "move"? I did. When you wrote
stuff like:

    
    
        let (move x, move y) = (move z.a, (move z.b).append(move z.c));
    

It got old fast.

~~~
Gankro
Admittedly there's still a syntactic difference between move and copy -- with
Swift and C++'s definition of copy. Rust calls that Clone, though. :)

------
ivoras
There was a new Hurd version announcement a few days ago and somehow Rust
reminds me of Hurd, but from a different direction: it seems to me that Rust
simply changes way too much to be currently widely accepted at any scale, and
has a high rate of attrition because of that.

Statements such as "But as of Rust 1.15, this restriction will be lifted, and
one of the largest blockers of people using stable Rust will be eliminated!"
and "It is unclear if there ever will be a Rust 2.0." (to avoid the "Python 2
/ Python 3 effect") don't help.

I'm comparing this to Go (yes, yes, both Rustaceans and Gophers will yell
"it's not the same!" \- in vain), where the Go 1.0 syntax was set in stone in
2012, and remained mostly compatible now, 5 years since, and _still_ it feels
that the community is somewhat sparse and reinventing wheels on regular basis.
The reality is that for complex projects and complex development environments,
once "Rust 1.0" syntax gets stabilized, it will probably take 5+ years for the
community to feel comfortable with it, and trust the language developers not
to break things at a whim.

Remember that the still widely-popular Python 2.7 was released in 2010 (and it
also sort-of set in stone the "Python 2 syntax"), and plan accordingly.

~~~
masklinn
It's really quite unclear what you mean, why would Rust _adding new features_
(syntactic or otherwise) be an issue exactly? You do realise the .7 in Python
2.7 is because every version before that added new features (and syntax) while
remaining backwards-compatible right? (well mostly, new keywords broke old
code).

> Remember that the still widely-popular Python 2.7 was released in 2010

I mostly remember that Python 2.7 is a straight descendent from Python 2.0[0],
and as far as I'm concerned a much better language for all that was added in
the meantime (though technically I only took up Python circa 2.3, a fair
number of syntactic and semantic additions had already been performed).

[0] and actually older than that, while 2.0 added major features the main
change was a switch towards a much more open and community-driven environment
with less single-organisation control over the project, it introduced PEPs,
sourceforge hosting of the tracker and source and a very large expansion in
commit bits from ~7 at CNRI to ~27 people around the time 2.0 itself was
released, _technically_ 2.0 is a minor update to the 1.6 which had been
released a few months earlier for contractual reasons.

