top | item 22448933

A half-hour to learn Rust

927 points| xrd | 6 years ago |fasterthanli.me | reply

148 comments

order
[+] talkingtab|6 years ago|reply
This is the most useful introduction to a language I have ever read. Often language introductions produce a "Wall of Complexity" and I failed in my last two attempts learning Rust failed because of that. This is just great.

Be warned, the "half hour" part is probably a bit like "99 cents" as a price tag. I've already spent more than that, but it is time well spent.

Thanks for writing this!

[+] chris_st|6 years ago|reply
I agree, this is a great page.

I failed to make it through the Rust book in (I think) 2017, and kind of hated it; I made it through easily in 2019 and enjoyed it... the book has improved that much. Oh, and the compiler error messages are better, too, which helps enormously.

[+] webew|6 years ago|reply
> This is the most useful introduction to a language I have ever read. Often language introductions produce a "Wall of Complexity" and I failed in my last two attempts learning Rust failed because of that. This is just great.

> Be warned, the "half hour" part is probably a bit like "99 cents" as a price tag. I've already spent more than that, but it is time well spent.

> Thanks for writing this!

[+] kzrdude|6 years ago|reply
Underscore is not exactly "throw away" but rather it is exactly "don't bind to variable". The difference becomes evident with the statement:

    let _ = x;

This is a no-op and the value remains owned by the variable x, and is not thrown away.
[+] fasterthanlime|6 years ago|reply
I debated which terminology to use and thought "throwing away" was more intuitive, especially if you've never heard of "binding" before.

It has its limits though - in your example I'd say you're throwing away the result of evaluating "x", just as if you did:

    x;
[+] nightcracker|6 years ago|reply
Underscore can also be the equivalent of a wildcard in pattern matching.
[+] da-x|6 years ago|reply
There's a difference between

    let _ = <expr>;
and:

    let _x = <expr>;
The first one drops ownership over the variable right after `let`, and the second drops in the end of the scope where the `let` is contained. It was mentioned in an open Rust github issue, but I can't seem to find it.
[+] watmough|6 years ago|reply
I did wonder about this, due to consumption of ... values / refs (terminology?) ... in Rust.

Thanks for adding this important detail.

[+] monocasa|6 years ago|reply
I guess I always viewed that as dropping a reference which is a noop, but still dropping.
[+] esjeon|6 years ago|reply
Nothing related to the tutorial itself, but I've seen many Rust basic tutorials recently, and this sorta remind me of Haskell - especially its Monad.

The Haskell community once was flooded with Monad tutorials[1]. People kept trying to put meanings on this mathematical construct, and had come up with tons of different ways to describe it. The problem is, this simple thing can fit into so many different places, so people couldn't possibly run out of new ideas. This oversaturation only confused newcomers even more, so the community concluded not to write any more Monad tutorials.

While individual tutorials enrich the community in general, it's more crucial to have a few good documentation that kill the need for more tutorials (e.g. MDN + W3School), so that efforts can be redirected into more productive things. Rustonomicon is one good documentation, but it always falls few steps short in practice. That's probably why more tutorials are being written, and why they get upvoted well in both HN and Reddit.

[1]: https://wiki.haskell.org/Monad_tutorials_timeline

[+] whatshisface|6 years ago|reply
The dust is settled and the fight is over. Which monad tutorial won "best monad tutorial?"
[+] krebs_liebhaber|6 years ago|reply
The way I like to think about the expression / statement divide in Rust is that everything is an expression, and the semicolon is an operator akin to `ignore` in F#; it takes any operands, disposes of them, and returns the unit type `()`.

Rust in general is just such a pleasant language. It can be a bit tedious to work with sometimes, but that's usually because the problem in question is tedious in and of itself, and you didn't even notice all of the little screw-ups that could occur until you saw how Rust dealt with it.

[+] 131hn|6 years ago|reply
Only, it’s more one hour and a half rather than half an hour. Excellent quality stuff nonetheless
[+] svnpenn|6 years ago|reply
Has anyone struggled with the lack of HashMap literal?

https://github.com/rust-lang/rfcs/issues/542

I just cant bring myself to doing something like this:

    let m1: HashMap<&str, i32> = [("Sun", 10), ("Mon", 11)].
    iter().
    cloned().
    collect();
https://doc.rust-lang.org/std/collections/struct.HashMap.htm...
[+] dmit|6 years ago|reply
Would a macro work ok for you, like the inbuilt vec! one? There's a crate for that named maplit: https://docs.rs/maplit

    let map = hashmap!{
        "a" => 1,
        "b" => 2,
    };
[+] taktoa|6 years ago|reply
You shouldn't be writing literal HashMaps in your code very often anyway; you should use a function that pattern matches on a string for the day-of-week-to-number example (returning Option of course). Most other cases where you might want literal HashMaps are well-served by either pattern matching functions or structs. The point of a HashMap is that it has keys that you don't know at compile-time; if you use a HashMap literal, that's basically saying that you do know the keys at compile time.
[+] j1elo|6 years ago|reply
I assume it must be a common finding. I have too wanted to initialize a map value with a literal, only to find that one needs to juggle around their absence. Very weird thing to miss in such a great language, really.

I guess that the syntax proposal would get too complicated with all the different possibilities (like what happens if you want to store one ref, now you need to add lifetimes and such), but overall for the novice it just looks like a strange thing to not have.

[+] dmitriid|6 years ago|reply
This is very good but... Description of traits suddenly dumps references, mutable references, reference bindings, dereferencing with zero explanation:

--- start quote ---

Trait methods can also take self by reference or mutable reference:

  impl std::clone::Clone for Number {
    fn clone(&self) -> Self {
        Self { ..*self }
    }
  }
--- end quote ---

What? What does this even mean? What is <asterisk>self, and why?

Too many questions, not nearly enough answers.

[+] dilap|6 years ago|reply
If you have a struct of type T and an instance of that struct t, you can create a new instance using the syntax

  T { ..t }
which means "a new T with all the fields set to their values in t".

Inside the trait "Self" is an alias for "Number".

"&self" is shorthand for "self: &Number", i.e., a reference to Number.

To dereference a reference, prefix with

  *
So:

  self
has type "&Number"

and

  *self
has type "Number".

Thus,

  Self { ..*self }
is creating a new instance of Number with its each set to the same value as self.

Disclaimer! everything I know about rust I learned 2 days ago from this blog post, so I might be wrong :-)

[+] unixsheikh|6 years ago|reply
What a nice approach to presenting the language in a clear cut way. We should have this for every language out there.
[+] abledon|6 years ago|reply
ive been perusing a bit of rust code for the past 2 years, reading an article here and there....always heard it was a complex and low level , next generation C language. comparing it to go etc... but never wanted to learn it... This article is _so_ simple in its delivery. It really got excited in the 'beauty' presented by each simple chunk.
[+] owaislone|6 years ago|reply
This is great write up. I wish every language had this on their official page. I'd personally love one for C++.
[+] 5Qn8mNbc2FNCiVV|6 years ago|reply
This is so good. I would love this kind of "learning" for every language. It's definitely not beginner friendly but for anyone that already knows a language this is a super introduction and quick start, maybe even tipping point to start using the language.

Only remark: If I had anchor tags at specific points creating a reference would be easy, this is referenceable material.

Great job though, I am amazed at the quality of this. Hell yeah!

[+] michaelangerman|6 years ago|reply
I am really happy I found this blog post. If you like this one you should check out some of his other posts. He has some amazingly insightful other posts on Rust including some very in depth topics. Also, a great comparison of Rust and Go. This is also why I love Hacker News. I would have never found these posts if this one had not made it to page one.
[+] donmcronald|6 years ago|reply
I want off Mr. Golang's Wild Ride [1] is a great read. The part about how different OS file permissions are handled in Go vs Rust is great. Even though I don't know Rust, I thought the Rust approach looked easiest since it's accurately represents the underlying systems. It was really surprising to see that Go's motivation in glossing over the complexity is to keep things simple. Is a half working implementation really simpler?

1. https://fasterthanli.me/blog/2020/i-want-off-mr-golangs-wild...

[+] jariel|6 years ago|reply
Every young language must have tutorials like this.

Start with the utter basics but move quickly through the common issues.

More than anything, these kinds of tutorials make it possible to at least start grasping the rest of the documentation.

I find with so many funky languages, the little examples aren't so hard, but there's too much 'unknown' syntax in the way to make sense of it.

These are essential.

[+] clarry|6 years ago|reply
I want books written like this. I've had too many books where I die of boredom over and over again in the first 150 pages and then I never read them again. It's like there's some rule saying books must be super wordy.
[+] TheRealPomax|6 years ago|reply
That rule is usually called the publication contract, and it usually literally says how big the work needs to be, even if you can say it better in fewer words/pages/chapters/etc.
[+] dmitshur|6 years ago|reply
I really enjoyed this, and it also inspired me to try a quick experiment: what would the same blog post look like for Go?

I tried it and wrote https://dmitri.shuralyov.com/blog/27. It was a fun exercise to go through.

[+] bogomipz|6 years ago|reply
Thanks for sharing. I enjoyed this. Could you elaborate on your closure example though? This was the only one I didn't understand. The explanation is a bit terse = "Closures are just variables with a function type with some captured context." Are these anonymous functions? What is calling the anonymous function if so in main? Cheers.
[+] vajrabum|6 years ago|reply
I did enjoy the article. It was quite clear but it made me wish for a bit more. Unlike the Rust article I didn't see any thing that suggested how golang might be special. I did see your suggestions for further reading though. I'll check them out.
[+] csomar|6 years ago|reply
Numbers could also be differentiated in Rust. For example, to make sure your "16" is an i32 you could write

> let x: i32 = 16i32;

Otherwise, it's a very comprehensive article. The only missing parts are the async/await keywords and the sync primitives (like Mutexes/RwLocks).

[+] GordonS|6 years ago|reply
C# has a similar feature, but it's less verbose, e.g. "16l"/"16L" is a long (64-bit integer), "16u" is an unsigned, 32-bit integer etc.

I find the rust syntax a little difficult to read. Take "16i32" for example - it's not immediately clear which part is the actual value.

[+] JoshTriplett|6 years ago|reply
You don't need both of those "i32"s. Rust has type inference, so you can just write either `let x = 16i32` or `let x: i32 = 16`.
[+] jononor|6 years ago|reply
Nice. What is dont understand is why is the second Vec2 used in this example? It introduces x and y, which are of type float right, not Vec2?

let v = Vec2 { x: 3.0, y: 6.0 }; let Vec2 { x, y } = v; // `x` is now 3.0, `y` is now `6.0`

[+] wizzwizz4|6 years ago|reply
Because you're ---unpacking--- destructuring a Vec2.

The most common form of this idiom I've seen is:

    if let Some(x) = foo() {
        println!("{:?}", x);
    }
where fn foo() -> Option<_>, i.e. foo returns either Some(_) or None. (if let is used to account for the possibility of None; it's technically different syntax to let, but very similar.)

Here, x isn't a Some (a variant of Option). It is, however, representing a value inside a Some, which we want to get out. Likewise with your example; we want to destructure from a Vec2, so we specify a Vec2 (with identifiers instead of data) on the left hand side and the data on the right hand side, and it takes the data out and binds it to the identifiers.

[+] dilap|6 years ago|reply
This is fantastic! Using it, I was finally able to write my favorite toy-problem in Rust (scoring a boggle board). Rust solution came out 25% faster than my (I thought) highly-optimized C++ solution, wow...
[+] jquave|6 years ago|reply
I've been writing Rust for about 2 years, I read this and learned all kinds of stuff I still did not know. Fantastic job on this! I am now a bit upset that everything isn't taught this well :o
[+] harel|6 years ago|reply
I wish all languages had this kind of page as a requirement of the language manual. Author, I will hold your beer...