
An alternative introduction to Rust - steveklabnik
http://words.steveklabnik.com/a-new-introduction-to-rust
======
BuckRogers
I'm a Python user, and always dipping my toes in other tech, working through
books on Go, wrote some Node servers and so forth. I rarely find something
that has enough advantages to warrant augmenting or replace Python or bring it
into 'my stack'. The time involved to really master these things isn't worth
it, in my opinion.

Something with no GC for hard real-time systems would fit the bill, also
something that I could write performance-critical libraries with a C ABI like
Rust to call from Python. I've had my eye on Rust and of course C(++), but
mostly waiting for Rust to stabilize before diving in.

Guides like this are going to mean a lot in the coming months for people like
me who are Rust-curious but don't have a systems programming background.

I'm not so much looking for yet-another-scripting language or GC'd language
but a systems language that perfectly augments my Python/SQL knowledge.

Thanks for doing this, Steve.

~~~
progman
You should consider the Nim language. It "feels" like Python but has seemless
integration with C, and native performance like C.

[http://nim-lang.org](http://nim-lang.org)

~~~
BuckRogers
I did at one time. I've been keeping my eye on Nim (though a far less watchful
one than on Rust) to see if the standard library works one day without using
the GC.

------
dfkf
In what sense this introduction is alternative? It's just like every other
intro that explains basic and easily understood things about the borrow
checker, but doesn't go into details on how you actually live and work with
the damned thing. The second thing you do after learning basics is trying to
implement some simple data structures, like lists, or even (horror!) double-
linked lists. In Rust this is the moment where you start hitting roadblocks,
and then you read somewhere that implementing "basic" data structures in Rust
isn't a basic exercise at all, and that is kind of discouraging.

~~~
comex
This seems like an unfortunate pathological case caused by the abstraction
level Rust sits at. The kinds of tree- and graph-like data structures that
garbage collected languages can represent in safe code (at the cost of
performance) don't work as well in Rust; you can still implement them with
various mechanisms (std::swap is very important for trees; Rc and Weak;
replacing pointers with indices into a global Vec; etc.), but due to a desire
to get optimal low-level performance, core data structures often end up using
unsafe blocks in their implementations. The thing is, though, implementing
data structures is something of a worst case: in most other code you can _use_
the data structures to provide the pointer relationships you need, but when
implementing them you have to do that yourself. Most other code is more
naturally amenable to working with the borrow checker - which is not to say
it's not difficult.

~~~
pcwalton
This isn't unique to Rust, in fact, as modern C++ presents the same dilemma.
Here [1] is a Stack Overflow question where someone asks how to make a doubly-
linked list out of smart pointers, and all the answers either involve
shared_ptr or C pointers. Same pedagogical issue: you want to deemphasize the
less-safe pointers (in Rust, the ones behind an "unsafe" gate; in C++, the raw
C pointers) in favor of the safer abstractions, but you hit a wall when it
comes to doubly-linked lists.

C++ has it easier in practical terms, I guess—few projects _actually_ use
smart pointers as pervasively as modern C++ guidelines stipulate (and they're
hard-wired into the language in a couple places like operator new and this),
so C pointers usually have to be taught pretty early anyway. In Rust, by
contrast, unsafe pointers are considered evil things you touch only when you
know you have to. But the problem is still there: it feels like something that
comes with the territory of manual memory management in general.

[1]: [http://stackoverflow.com/questions/15384443/doubly-linked-
li...](http://stackoverflow.com/questions/15384443/doubly-linked-list-using-
stdunique-ptr)

~~~
pjmlp
I don't get why they just don't suggest using weak_ptr<>() for the backwards
reference.

~~~
Animats
You need to use weak pointers for backpointers in Rust, because there's no GC.
If you create a cycle with reference counted objects, objects are never
released and the program will leak memory.

~~~
pjmlp
Of course, but my question was _why it wasn 't explained_, I know very well
the reason why weak pointers are required.

------
kbenson
I suggest you go with prose that clearer over prose that is more stylistic.
For example:

 _Line six has a closing curly brace, and so main, and thus, our program, is
over._

Could be rewritten as:

 _Line six has a closing curly brace, and so main is over. By extension, our
program is over as well._

Maybe even with a quick reinforcing of the (hopefully earlier explained)
concept that main is the main program block.

It's a fine line to walk, making the text interesting while also making it
_very_ clear, but I think there's something to be said for making the English
easy to parse, since the people going through the tutorial will have enough on
their hands dealing with the code examples.

~~~
jonnybgood
I agree. Dedicated tutorial authors of languages with advanced concepts such
as Rust, Haskell, etc. really need to take a course or have a quick overview
of technical writing. Steve does a pretty good job with Rust, I think. I wish
Haskell had good tutorial authors like Steve. Haskell really needs better
tutorial authors.

Are CS majors typically required to take a technical writing class like other
engineering disciplines?

~~~
iopq
I had to take one. All I remember from it is making posters and graphs. We
didn't actually learn BETTER WRITING in it, but how to write RFCs.

------
hackuser
> Rust’s variable bindings are immutable by default, but can become mutable,
> allowing them to be re-bound to something else

From someone reading about these details for the first time:

Calling them _variable_ bindings confuses the matter. You say, just moments
earlier, that _variables are called by that name because they can change over
time, they’re mutable_. But this _variable_ is immutable? But only sometimes?
!

I know you mean that the mutability varies and that it makes sense in the
context of the jargon of functional languages, but that's something that will
make sense to language developers who have worked on Rust for years and maybe
to the HN crowd, and not to new coders trying to grasp difficult, novel (to
them) concepts.

How about "multimode bindings" or "multiphase bindings" (as in liquid/solid
phase transitions) or dual-mode or something more entertaining like "double-
agent bindings". Is it too late to change?

Maybe I'm missing something, but I suspect years from now people will be
saying, 'so _variable_ bindings are not always variable? huh?'

~~~
dbaupp
'Variable' is a jargon-y term in mathematics/programming in general, it
doesn't literally mean "this thing can vary in the run of this program", e.g.
even Haskell calls its name bindings "variables", and it is focused on
immutability much more than Rust (I suppose this is the functional programming
aspect you were mentioning).

One way to view it is that the bindings don't have a value that is fixed at
compile time: they vary between runs.

In any case, I'm not sure what you mean by "mutability varies".

------
shorodei
It definitely feels a little long winded for a person with some background
already in systems langauages like C. Do you plan a TLDR for systems language
people?

~~~
steveklabnik
A Redditor put it best:

    
    
        > My only concern is that lot of the motivating of the ownership
        > system seems to be directed at C/C++ programmers, but at the same
        > time you're explaining pointers and memory management as almost new concepts.
    

I think this is a weakenss of this draft, yeah. I want to make it accessible
to non-systems people, but also okay for systems people. In the current,
actual intro, I treat everything at a high level, looking downard
occasionally. This version is more about starting down and looking up, but it
might be not enough detail for the non-systems crowd (there's still tons of
implicit knowledge here, even though I tried to spell it out) and a bit
tedious for the systems crowd (who have intenrnalized much of this already.)

Writing is hard.

~~~
jk4930
What about two intros? One for the low levelers, one for the high levelers?
It's extra work, but it allows to treat more appropriately what each side
needs to learn.

As [1] says: "Rust for C programmers != Rust for functional programmers."

[1] [http://science.raphael.poss.name/rust-for-functional-
program...](http://science.raphael.poss.name/rust-for-functional-
programmers.html)

~~~
steveklabnik
Also very possible. It's something I've been considering for a long time.

------
deckiedan
I really like your writing style - it's a little verbose in places. The
paragraphs detailing what each line number does would be easier just as
pictures / diagrams with arrows to the source code sections. Likewise quite a
lot of this could be made mode succinct, which would aid readability.

Along with that, it's really hard for me to get used to right-hand line
numbers. Shoving them along the left hand side like most editors would be much
much much easier to read for me, at least.

That said, I do enjoy your writing and find it quite readable.

~~~
steveklabnik
Thanks! It is a bit verbose, that's what I was going with with this version.
You have to explore the extremes before finding a comfortable medium, you
know?

I do agree better diagrams would help, for sure. And yeah, the right hand
numbers were kind of a compromise: they kept the exercises compileable without
needing to mess with markup stuff, which is just a weakness of markdown :/

~~~
AndrewDucker
What confused me was that the piece just seemed to stop, without a proper
ending. It told me that "Rust stops you doing X", but not "And here's how to
do it correctly in Rust."

------
glesica
Steve, really great work, I've appreciated all your efforts as I've played
around with Rust as it approaches 1.0. I think this kind of really deep
treatment of an important topic belongs _somewhere_ in the introductory docs.
I especially like that you give concrete examples of the problems that Rust's
concept of ownership solves. The memory diagrams are actually quite helpful
(IMHO) regardless of the "direction". Keep up the great work!

~~~
steveklabnik
Thank you!

------
eslaught
I wonder, for people coming from non-systems programming languages (e.g.
Python), whether it would help to visualize what is actually happening under
the hood in a scripting language and comparing that to Rust. As others have
noted, this guide is written as if to C/C++ programmers, while the intention
(I had thought) was to also target scripting language folks?

My experience, having started in C++, then gone to scripting languages, then
come back again, is that it can be quite helpful to consider what actually
happens under the hood in a scripting language. Because Python's references
are more limited, you would never exhibit the same bug that you illustrated in
C++, but there are still issues around e.g. aliasing which tend to trip up
inexperienced Python programmers, and where you could illustrate interesting
things about Rust's ownership model. And boxed/unboxed data types plus
reference counting (in Python) would give you an excuse to talk about RAII. So
I think that you could do this in a way that would actually speak to savvy
scripting programmers, while still being definitively systemsy.

Just a thought.

------
losvedir
Hm, I too got the sense that it's not clear what the "prerequisites" are for
reading the article. Parts of it were really straightforward to me "oh, I know
roughly what a stack and the heap are" to confusing "wow, I don't know
anything about C++". I think a few distinct guides targeting different sorts
of programmers might be the answer here.

But I do like the idea of focusing on ownership and lifetimes more front and
center, since that's rust's raison d'etre.

Can you get a hold of Niko's slides from the NYC meetup this week at
Bloomberg? I was there, and his overview of ownership and borrowing, using the
examples of a book, were actually really helpful for me. For whatever reason,
his approach really resonated with me.

------
Animats
It's still too confusing. Try drawing actual pictures to illustrate the
scope/lifetime issues. Talking about addresses on the stack is an
implementation detail.

Things to cover:

Basic lifetime concepts in Rust:

    
    
       1.  Scopes are nested.
       2.  Variable lifetimes are tied to scopes.
       3.  Unlike other languages, variables can be created
           tied to a scope outer to the one where they are
           created. The user can control this by indicating
           that the lifetime of a variable is the same as that
           of some variable from an outer scope. This is Rust's 
           big innovation.
       4.  A function can create a new object and return it by
           reference if the function explicitly says that the 
           new object has the same lifetime as some parameter.
    

Basic ownership concepts in Rust:

    
    
       1.  There are three kinds of ownership - single ownership, 
           reference count multiple ownership, and thread-safe reference 
           count ownership.
       2.  The reference count ones work like you'd expect.
       3.  Single ownership interacts with the lifetime system in 
           to make it possible to create data structures such as
           trees, with all components single ownership and having the
           same lifetime.
       4.  The "=" operator usually means "move", and when it does,
           the right hand side of the "=" becomes inaccessable.
           (This is type-dependent; some types copy when "=" is used.)
           This preserves single ownership.
    

Basic data structure concepts in Rust:

    
    
       1.  Rust has enums, which, unlike enums in other languages, can
           have data objects associated with them. They're really variant
           records.
       2.  You can declare an enum with a data object as a field in a 
           structure. This creates a structure that contains (really links 
           to) another data object.
       3.  This can be done recursively, so you can create trees of
           structures.  
       4.  You can't violate the rules of single-ownership.  Trees are OK;
           directed acyclic graphs or backpointers are not allowed using
           single ownership.
       5.  There's no "Null". Instead you create enums which have one value
           with an associated data item and one without. 
       6.  Everything has to be initialized to a valid value for its type.
    

Basic borrowing concepts in Rust:

    
    
       1.  You can create temporary references to something with an equal
           or longer lifetime, because the temporary "borrowed" reference
           cannot outlive the thing it is referencing. That's safe.
       2.  You can have one writer (a mutable reference) or N readers 
           (immutable references) at a time.
    

Basic type concepts in Rust:

    
    
       1.  It's complicated.
       2.  There's automatic type inference.  Not just for "let", either.
       3.  Function parameters have to be declared, not inferred,
           except for the special syntax for closures that lets you write
           concise lambda expressions.  This limits how hard the
           type inference system has to work.
       4.  I don't fully understand Rust's type system yet.
    

Basic safety concepts in Rust:

    
    
       1.  All of the above is enforced at compile time.
       2.  The result is memory safety.
    

Misc. notes:

    
    
      - Lifetimes are novel, but not that complex.
      - The type system really is complex.
      - There are generics and you'll need them more often than in C++.
      - The compiler first checks syntax, then types, then borrowing. It does not report
      borrow errors until everything else is correct.
      - There are no exceptions, and proper error handling requires rather verbose code.
      - Have a plan for your variable lifetimes before you start coding, or you end up
      fighting with the borrow checker and losing. 
      - If you think you need "unsafe" in pure Rust code, you're doing it wrong.

~~~
dbaupp

       3.  Unlike other languages, variables can be created
           tied to a scope outer to the one where they are
           created. The user can control this by indicating
           that the lifetime of a variable is the same as that
           of some variable from an outer scope. This is Rust's 
           big innovation.
    

This isn't quite right (or I'm not understanding what you're trying to say).

Given

    
    
       let x = 1;
       {
           let y = 1;
       }
    

there's no way you can make `y` itself live as long as `x`.

    
    
       4.  The "=" operator usually means "move", and when it does,
           the right hand side of the "=" becomes inaccessable.
           (This is type-dependent; some types copy when "=" is used.)
           This preserves single ownership.
    

I think this is "complicating" how =/moves work: it's really binding things
by-value that are moves, the = operator isn't special. E.g. `foo(x)` will move
`x` into the `foo` call, despite there being no `=` at all.

    
    
       2.  You can declare an enum with a data object as a field in a 
           structure. This creates a structure that contains (really links 
           to) another data object.
    

I don't understand this, could you clarify?

    
    
      - The type system really is complex.
    

I don't think this is very true, what complexity do you see?

~~~
iopq
I think traits are really confusing (dispatch by input AND output type),
there's also trait inheritance, trait bounds, casting things to traits; add
type inference to that, lifetime parameters and generics. You get things like

    
    
        struct Foo<'a, P> 
            where P: std::ops::Neg + 'a,
               <P as std::ops::Neg>::Output: 'a {
            foo: &'a Bar<P>,
        }
    

after further research this makes sense, of course, but looks like non-sense
to anyone just trying to learn the language

~~~
dbaupp
Hm, maybe I just "know too much", but it seems to me that most of the parts of
the type system are relatively independent: there's not too many weird
interactions between the various parts, and many systems are complex because
of the surprising interactions.

In any case, half the "complexity" in the example you give is the noise of
using the fully-qualified std::ops::Neg path in the bounds, rather than
importing it and just writing `Neg`. :)

~~~
baq
TBH whenever I see type names being added to each other, my brain kind of
shuts down the relevant lobes. Granted, I'm barely initiated to Rust, but...
it just looks confusing. (It doesn't help that it's not actually type names,
but a type name and a lifetime...)

------
comex
I agree with some of the caveats listed by other people here (in general, that
this guide seems wishy-washy about what type of programmer it's catering to),
but I like the memory layout tables. While I don't remember much about how I
thought at the time I might have benefited from such a tutorial myself, when
these days I mess around with exploits and the like which do weird things to
memory, it often feels surprisingly grounding and illuminating to open up a
debugger and start printing the contents of memory, even if on a theoretical
basis I already know what it's supposed to look like.

But please make it clear that the tables don't accurately describe the real
layout of memory in a compiled Rust program. Heap mappings and thread stacks
are typically at random (thanks to ASLR) addresses, rather than the start and
end of memory, and the locations of specific heap allocations are usually
based on complicated rules depending on the size class of the allocation
(differently sized objects never get allocated together in most allocators),
thread caches, ordering of buckets within a page, etc. (Some architectures
also grow the stack the other way.)

~~~
steveklabnik
Yes, it's always about the right level of abstraction. Making it clear that
it's not _actually_ 0x00 would be good, but then you can get into discussions
about virtual memory, and all that other stuff that you're talking about,
which just muddies the point of the example. It's a fine line.

------
mkishi
I like the new structure much better!

Now, I'm in no way a writer. English isn't even my main language.. So take
this next point accordingly:

Unfortunately, I get an even more condescending vibe from this draft than I
got from the book. Not as in "I'm an expert and you're treating me like a
newbie," but as if I was a kid instead of an aspiring Rust programmer
(beginner or otherwise). I wish I could point out -exactly why- I get this
feeling, but I'd be lying.

I do think I'm starting to see a pattern, though, that perhaps contribute to
this gut reaction. The pace between concept introductions is odd, somewhat
erratic. At times, in one line you're talking to a complete novice programmer,
and the very next you assume they know other languages and general concepts
[1]. Other times, you take some basic knowledge for granted and explains it
right after [2]. I know it must be very hard to find the right balance for
your target audience, but I found these jumps very jarring. For what it's
worth, once you get past the introductions you pick up a more to-the-point-
explanations rhythm and that reaction goes away.

[1]: For example, after several paragraphs and two code snippets to introduce
the concept of 'variable bindings' in a novice-friendly way, even going as far
as (somewhat) explaining what system and functional languages are, the text
suddenly assumes I know about program execution and what a control flow
structure is?

> As you know, a computer program is executed line by line. At least, until
> you hit a control flow structure, anyway.

This line was specially off-putting. After all that explanation, the
assumption I'd know this made me feel like a kid. But I can only imagine how
inaproppriate I'd feel if I didn't know. Given the novice level of the text up
to then, the 'as you know' sounds very out of place.

[2]: The very next paragraph, we go over a program with one variable binding
line by line. And then, it's assumed I know about scopes. But after that they
are explained anyway. I realize this is a way to go over every concept, even
if one already knows them, but it feels odd! Both beginner and experts would
understand a plain explanation. But this back-and-forth about how to present
concepts doesn't work -- for me. It'd make more sense to read straight-forward
explanations of what I already know than these "as a programmer, you know that
x," mixed with even simpler concepts being introduced as new.

This sounds harsher than I planned -- I'm no writer afterall! But, really,
despite these gut reactions, the Rust book is turning into one of my
favorites. Thank you for all your work!

~~~
steveklabnik
> I wish I could point out -exactly why- I get this feeling, but I'd be lying.

It's hard, for sure. There are certain sentence structures that strike certain
people this way, and my style sometimes triggers them. For example,
"basically" has no negative connotation for me, but really gets some people
going, so I've been eliminating it wherever possible. I wonder which aspect
does it for you... thanks for the brain dump, this is helpful.

------
nikitakit
I liked the structure of the guide. However pairing the memory tables while
mentally executing the code line-by-line got confusing for me. (Especially
because the code was offscreen because the tables take up vertical space)

I'd love to see this kind of explanation done in a non-text medium. For
example, a slideshow or interactive webpage (ex:
[http://www.redblobgames.com/pathfinding/a-star/introduction....](http://www.redblobgames.com/pathfinding/a-star/introduction.html))

~~~
steveklabnik
I really wish I was better with front-end tech, creating those kinds of things
would be awesome, but above my skill level, unfortunately :/

------
lelandbatey
I was actually reading the rust lang book for the first time yesterday,
specifically the intro to lifetimes that this post is a revision of.

I'm not sure jumping so much into what's going on in the C++ side is a great
idea. The section as it stands now covers the important points (with out
lifetimes invalidating pointers is easy) without getting too lost. However,
I'm already pretty familiar with C++, so the more brief explanation may not be
enough for everyone.

------
gnuvince
I like it, but I would make two suggestions

1) There are many memory schemas for the push_back example, maybe you want to
organize them side-by-side with an arrow between them showing which statement
caused the change. Saving some vertical space may do some good.

2) I'm not a fan of the sentence structure in many places. Needs more pith.
Perhaps apply some Strunk & White and some K&R.

------
TazeTSchnitzel
That was great, I was completely unfamiliar with Rust's borrowing system and
now I think I understand it!

~~~
steveklabnik
Thanks!

------
cousin_it
Steve, great work! I know how hard it is to write so clearly.

The only quibble is that I'd make the middle slog a little bit shorter, maybe
laying out the vectors horizontally instead of vertically, so people wouldn't
forget what the text was about by the time they get to the end of the diagram.

~~~
steveklabnik
Thanks!

Yeah, the layout could be much better, and it'd be neat if I could use real
diagrams with arrows as the pointers, maybe.

~~~
surrealize
I thought this intro started really clearly, but I did get bogged down a bit
matching up the pointers as numbers. So yeah, I think diagrams with arrows
would be a lot easier to follow. I remember seeing a talk from Niko with
animated box/arrow diagrams that changed as a piece of code executed
(alongside a code snippet with the relevant code highlighted). That was super
slick, and I thought it really helped make things clear.

Also, is it standard to talk about memory growing "down" as the memory
locations numerically increase? That's the opposite of my impulse (I would
have said that 0xff is the top of memory, and 0xfe is "down" from there) but
TBH it's been a while since I took the relevant CS class.

------
ilaksh
So basically, anyone who doesn't already really appreciate Rust must not know
what a stack is, what a pointer is, or what immutability is?

I'm sorry, but this implication enrages me.

If I'm just trying to print out "Hello World", why would I use some kind of
list and a pointer?

You touched on an example of where Rust simplifies non-GC memory management.

Its nice that there are various ways to track when you are done with pointers
(and memory referenced by them) across the code.

I think some of the techniques are very useful. But its taken to an extreme in
Rust where you will do anything to avoid GC.

Some of that stuff involving tracking and matching ownership or correspondence
across the code would work better outside of a purely sort of one-dimensional
textual representation. This is an example that illustrates why textual source
code is a legacy.

But it IS text, and often, with Rust, its too complicated, and often not
easily applied to typical "systems" programming (which is quite often used to
actually mean some ordinary types of user applications, or programs that do
the same thing).

Personally I think Rust could maintain a good portion of its core strength
while losing about half of its complexity.

Also, in terms of an introduction it would be nice to see some real useful
"systems programming" examples where the core memory tracking and other
capabilities make Rust stand out. Hello World isn't a good example of this.

We want the compiler to be smart and be able to avoid collecting things at
runtime when possible and practical, and for the programmer to be able to
effectively indicate corresponding data so the compiler will know when it
should be deallocated. However, we also need the code to be clear and concise
and to avoid making the programmer do routine work when its not necessary.

~~~
steveklabnik

       > while avoiding about half of its complexity.
    

If you know how to do this, while staying within Rust's design goals, we'd
love to hear about it. I think Rust is no more complex than the problem
demands, but I'm biased!

------
rafaelferreira
Like the writing style and the detailed play-by-play traces. A small tweak
that would help to make it easier to read would be to visually highlight what
changed from one memory snapshot to the next.

------
EugeneOZ
That example of aliasing - is it possible to write such thing in Go? Will Go
compiler eat it or will not compile? Just interesting, how Go behaves in
comparison to Rust, as another modern language.

~~~
shadowmint
You can have aliasing pointers in go; see
[https://gobyexample.com/mutexes](https://gobyexample.com/mutexes) for how you
have to explicitly lock to update the target without undefined behaviour.

However, since it's a GC, it's probably not possible to have a pointer that
refers to a memory location that doesn't exist, because having a pointer to a
memory location is _by definition_ what keeps it from being free'd by the GC.

~~~
EugeneOZ
thanks. but time between 2 lines of code can be not enough for GC to be called
to free first address. Maybe they just control it on language level.

------
baq
What I feel is lacking in rust tutorials is when it's ok to use unsafe code.
It's very hard to get a feel for that.

~~~
bjz_
Huon (dbaupp) has a nice overview: [http://huonw.github.io/blog/2014/07/what-
does-rusts-unsafe-m...](http://huonw.github.io/blog/2014/07/what-does-rusts-
unsafe-mean/)

------
xiaq
> You highlight lines four three six.

I guess there is a typo and should be "four through six" instead.

------
darklajid
Like it, but contains some typos.

becuase

langauges

ownerhsip

------
DevFactor
Alternatively, my video introduction to Rust should be live soon:
[https://www.youtube.com/user/DevFactor](https://www.youtube.com/user/DevFactor)
Always great to have a few viewpoints.

I 'kudos'ed your post though - because I thought it was very nicely written!

~~~
callahad
I'd appreciate it if you provided a bare link to your channel instead of one
that pops up a Subscribe modal.

~~~
DevFactor
Of course, my bad!

