341 comments

[ 3.2 ms ] story [ 424 ms ] thread
A half-hour to learn Rust Jan 27, 2020 · 51 minute read · rust

maybe the site's reading time estimator is broken? sarcasm intended.

But seriously, it is good to have people writing things like this.

half-hours is only the compilation time.
Sarcasm aside, the Rust compiler has gotten a lot faster (and parallelizes better) over the last year thanks to Nicolas Nethercote and others.

Another thing few people realize is that the "incremental" compilation mode that's the default for debug builds can also be enabled for release builds!

In CI, something like sccache can help a lot (using the GCS or S3 backend). It makes GitHub Actions' two-core limit almost bearable. Almost.

I'm guessing the code snippets are skewing the estimate.
If anything, code snippets take time to parse and understand what every word and symbol mean. I kept going over "traits" because I didn't get them at first.
> maybe the site's reading time estimator is broken?

All of them are. I first started disliking Medium for just that reason.

Programmers don't read, they skim in half the time, then auto-complete in their mind with false assumptions!
(comment deleted)
Is there a name for this behavior? Sounds like something that is categorized as a cognitive bias?

I'm confident that this does not only apply to programmers :)

It's the cognitive bias cognitive bias (the idea that every random mistaken behavior can be categorized as a congnitive bias).
That would explain so many of the comments you read on HN and Reddit!
And then try to write a GUI in Rust.
The state of that art is improving quickly: https://github.com/hecrj/iced
Doesn't do localization or integration with assistive technologies of the host platform, does it?
Maybe the state of the art for immediate mode GUIs, but not everyone is hopping onto that bandwagon.
Ouch! And just in time for New Year's resolutions. Thanks!
Firefox' reader mode estimates it at 35-45, if that helps :-)
>maybe the site's reading time estimator is broken

No, it's just made for prose, not tutorials.

Yep, code is a lot of "words" which throws off the estimate. I need to address this, but I don't think completely ignoring code blocks is really the solution there.
Maybe you could estimate the reading time of a code snippet by taking the square root of the number of lines of code? I imagine that the larger the code snippet, the more irrelevant boilerplate it contains, so the square root might be a good way to model this
I worked my way through the entire article, typing in each code example. Took me about 3 hours.
Well, it does say "reading time"; I don't think the estimator expects people to write the article as well!
> maybe the site's reading time estimator is broken? sarcasm intended.

It was not rewritten in rust?

Incredible. Great job.

Oh, I guess it's a year old - but still, extremely good.

It only took me a couple of hours to port one of my old C programs, an utility to produce a hexadecimal and ASCII dump to Rust. The Borrow checker didn't even faze me. All I needed was plenty of Google-fu and my coding skills. JetBrains CLion and git are also very useful tools.
I had a similar experience porting a relatively straightforward and simple CLI utility I had written in C.

Having said that, when I've faced real challenges with Rust (and the borrow checker) it's been with bigger longer running applications, like a webapp, or a long running service.

I have no doubt part of that comes I have a stronger background in garbage collected languages, so my mindset when developing larger applications is in that mode. I'm sure with enough practice I'd get it, but there were many things that I just couldn't replicate one to one in Rust. Not that they weren't possible, but they were just different enough that I couldn't figure it out without being more comfortable in understanding the language, and I just haven't dedicated the time needed to it.

Will likely go back to it at some point, as it is an interesting approach to programming, with lots of upsides.

My biggest issue was trying to print the contents of u8 as a character. In the end this was actually as simple as writing print!("{}", line[i] as char), no print specifiers needed.

I may do another more complicated port soon. I love CLion.

> It only took me a couple of hours to port one of my old C programs, an utility to produce a hexadecimal and ASCII dump to Rust.

Writing C code with Rust is a very different experience than converting C code to idiomatic Rust. There is more to porting than doing 1:1 conversions between keywords.

And by your description your utility basically reads words and prints their value. That doesn't really venture too much beyond the hello world territory.

u8s, actually. But yes, you are right that was a very simple port. I to plan to do a port that's more complicated soon.
The first I'm hearing of the "borrow checker"... so I started reading this https://blog.logrocket.com/introducing-the-rust-borrow-check..., and I got to the part where it says

"Your programs have access to two kinds of memory where it can store values: the stack and the heap."

They left out variables, which is odd.

How are variables not in either the stack or the heap?
When you write a program, heap variables are allocated using NEW or Malloc... stack variables are local to a procedure or function, the rest are in the Var block before your code, and are neither on the heap, nor the stack. They're global to the program, or the unit.
You mean global/static variables?

Yeah, those are not mentioned. It also doesn't mention constant storage...

That said it's not like they're the bread and butter of programming for their lack of mention to be that "odd" as the parent implies.

Wouldn’t that mean they’re on the heap by default? Your program will have to malloc something if they don’t go on the stack.

(My low level knowledge is limited, may be completely wrong)

In some (interpreted mostly) languages they are. Like some also don't have a stack at all.

But in the most common languages, there is special storages for globals, statics, and constants, which is what the grandparent means (e.g. the DATA section).

In the 9th code snippet that's not an example of variable shadowing, it's just variable reassignment. Shadowing involves variable assignments in different scopes.

https://en.wikipedia.org/wiki/Variable_shadowing

edit: I should've called it "variable redefinition" or something like that I guess, my mistake. Reassignment is definitely not the correct terminology for this.

Still, it's not shadowing because the new binding of 'x' is not effectively shadowing some other 'x' name, it's just taking its place in the same scope. And this is orthogonal to the memory allocation of the assigned objects.

There is effectively an implicit scope created by the let binding. In rust you can't reassign a variable without it being mut. The two `x`s are separate variables named x and are not at any point mutated. Reassignment would look like this:

    let x = 1;
    x = x + 1; // <- error[E0384]: cannot assign twice to immutable variable `x`
    let mut x = 1;
    x = x + 1; // ok!

This obviously matters very little for an integer, but it is relevant to more complex types.

You can actually see the scopes, and the progress of variable liveness, if you run the compiler out to the MIR intermediate language:

    fn main() -> () {
        let mut _0: ();                      // return place in scope 0 at src/main.rs:1:11: 1:11
        let _1: i32;                         // in scope 0 at src/main.rs:2:9: 2:10
        scope 1 {
            debug x => _1;                   // in scope 1 at src/main.rs:2:9: 2:10
            let _2: i32;                     // in scope 1 at src/main.rs:3:9: 3:10
            scope 2 {
                debug x => _2;               // in scope 2 at src/main.rs:3:9: 3:10
            }
        }

        bb0: {
            StorageLive(_1);                 // scope 0 at src/main.rs:2:9: 2:10
            _1 = const 1_i32;                // scope 0 at src/main.rs:2:13: 2:14
            StorageLive(_2);                 // scope 1 at src/main.rs:3:9: 3:10
            _2 = const 2_i32;                // scope 1 at src/main.rs:3:13: 3:18
            _0 = const ();                   // scope 0 at src/main.rs:1:11: 4:2
            StorageDead(_2);                 // scope 1 at src/main.rs:4:1: 4:2
            StorageDead(_1);                 // scope 0 at src/main.rs:4:1: 4:2
            return;                          // scope 0 at src/main.rs:4:2: 4:2
        }
    }
https://play.rust-lang.org/?version=stable&mode=debug&editio...
Oh interesting, I see now! Thanks for clearing this up.
There is literally a Rust example in your wiki link saying that this is shadowing. You can even redefine the variable to have a different type, how can this be reassignment.
No, it really is shadowing. The second `let` allocates a new memory location in the activation record; all future mentions of the variable name will refer to the new location. The old location still exists, and may have references to it already; references that will continue pointing at the old allocation and will not see anything that happens to the new one. If the old value has a `Drop` implementation, it will run after the second allocation’s `drop()`. The two `let` bindings can even have completely different types.
Isn't variable reassignment a special case of variable shadowing, where the new scope closes at the same place as the parent scope?
In Rust, shadowing is when you "declare a new variable with the same name as a previous variable" [1] -- even in the same scope. It seems it "fell out" of how the original compiler was implemented, and there was more support to retain it than not. See: https://internals.rust-lang.org/t/history-of-shadowing-in-ru...

[1] The "Rust book" has a good explanation of shadowing, which contains a nearly identical example. See: https://doc.rust-lang.org/book/ch03-01-variables-and-mutabil...

(comment deleted)
This is actually fantastic.

Wish more language tutorials were exactly like this, no fluff.

That is the best article on Rust I've ever seen. Better than the Rust book. I've been saying that Rust needed a book that wasn't written by the designers of the language, who are too close to it. Now we have one.
But it only covers the very basics. Disclaimer: I've only scrolled through it, but the code snippets are all small. It does show an admirable amount of Rust's syntax, but it's not very discoverable, whereas the book is reasonably well structured (although not for novices, who have forgotten a specific term). The link also doesn't deal with larger programs: there's not an Rc<T> on the page, let alone something like an Arena and unsafe code, and it's really necessary for complex programs.

So, IMO: not better than the Rust book. It gives a first glance of Rust, something to accustom the eyes to a different syntax, but sidesteps the difficult bits, which can make transitioning so frustrating.

There are plenty of cases where you can write a complex program in Rust without ever needing those things.
I've favourited the article. It's a good complement to the Rust Book, which has more detail. This kind of summary doc is great for discovery, you skim it and you find something that you could have written differently, or you realise there's an idiom you weren't using.

No substiture for the book, though.

It's your opinion and I'm not arguing: a lot of people love the Rust book, but some (including me) simply can’t read it because of the style of writing and cases when you discover some interesting and deep topic but the author just gives 1 example in 5-10 lines of code and that's all. I think that things like Mutexes or Boxes are deep enough to have 30-50 pages dedicated to them, and I can say this not only about Mutexes and Boxes. When explaining some powerful things, one need not only explain how they work, but also what kinds of real tasks they can solve - ways of usage are obvious for you when you know it and have experience of usage them, but absolutely opaque and not obvious when you reading about them for the first time.
I highly recommend Jon Gjenset's streams for more advanced Rust concepts like lifetimes or interior mutability. He goes into depth explaining how they work, what kinds of problems they solve, and gives tons of great examples.
I have a lot of Rust articles[1] that go into a lot more depth. They're mostly adventures though, we learn about ICMP, ELF, file systems etc.

Some love that style (the detours, the stream of consciousness) and some can't stand it. The Rust book is excellent, and a completely different style — they're complementary!

[1]: https://fasterthanli.me/tags/rust

I don’t think you should have much unsafe code as a rust beginner.
(comment deleted)
You may be also interested in the many existing teaching resources published by non-language-designers (whatever that means for a community-driven project like Rust), such as Rustlings and Rust by Example at https://www.rust-lang.org/learn if the book doesn’t satisfy whatever criterion you think disqualify it. Some of it is published via the rust-lang.org website, but it was/is maintained by different people who work on the language, compiler, std lib (etc). The project has a large number of people across many teams: https://www.rust-lang.org/governance
There are a few other well regarded resources. Programming Rust from Blandy, Orendorff, and Tindall/O'Reilly is great if you have lots of knowledge in C/C++. Rust in Action from McNamara/Manning is great if you prefer to learn by building projects.

Disclaimer: am the author of Rust in Action

Rust Crash Course by Michael Snoyman is great. You can get a free copy at fpcomplete.

Did a free course in December with him, and I'm still watching the classes because it has so much great info there.

Also I do recommend the Rust Programming, it really teaches you "how/why" use Rust.

To conclude, rust-learning (at GitHub) have great links, amazing articles about parts of the language, there is so much knowledge there, just go collect it :P

Out of curiosity, what don't you like about the Rust book? I personally loved it, reading about a year and a half ago. I thought they covered most of what I needed, was well written, and had plenty of good examples.
Not all writing clicks with every reader, it is impossible.

He previously said “That's too hard for an introductory book and not detailed enough for a reference manual.” It is very difficult to get this right, given our goals, and so reasonable people may think we fall short there.

I'd LOVE it if we could get a Why's Poignant Guide To Rust... I find that kind of weird humor helps my mind remember things.
This article is very very basic beginner Rust. You'll be disappointed if you expect to encounter Rust in real world like this article.
> And here's a toilet closure: |_| ()

> Called thusly because |_| () looks like a toilet.

Pure poetry!

Real talk though, this is a great introduction.

I often use the "owl" <(),()>, usually in Result<(),()>.
Why use a Result whose error type is empty?
I should have said :) I am writing backtracking algorithms (think something like a Sudoku solver where we fill in values by guessing), and an 'Error' is when we can deduce the Sudoku cannot be filled in, so we have to backtrack.

I find Rust's ? notation gives a very natural way of writing such algorithms. I don't care "why" filling in the Sudoku failed, particularly because the solver will typically fail millions of times a second, so I definately don't want constructing the error to be expensive in any way.

I would suggest Option is the more semantic type to use for your situation - None indicating no valid solution on this path
Oh, today I learned the ? operator works with Option as well as Result! I might try that out, thanks.
IIRC, it didn't work with Option in the past, so you might have been correct when your code was written. The ? operator came originally from the try!() macro, which was specific to Result; unlike the macro, it was designed to be extensible through the still-unstable std::ops::Try trait, but IIRC that trait was initially implemented only for Result, and only later was extended to Option and a couple of others.
It's sometimes called a type-level boolean. Instead of trying to remember what true or false means, you have explicit variants associated with success or failure. (The underlying representation, including bit width, is the same)
There wasn't anything on concurrency or asynchronous programming (like a http client). Is there a good resource for those parts of the language?
(comment deleted)
> There is a special lifetime, named 'static, which is valid for the entire program's lifetime.

I've heard this many times before, but as the guide[1] says:

"You might encounter it in two situations... Both are related but subtly different and this is a common source for confusion when learning Rust."

'static means different things for references and trait objects.

ie.

'a: 'static --> reference with lifetime 'a lives forever and can never be dropped.

T: 'static --> Type T has no references in it; it is effectively 'entirely owned' by the owner of an instance of T.

These are totally different things with the same name.

(I believe technically there are actually three; `<T: 'static >` is a type constraint and `T + 'static` is a trait bound, but that both mean the same thing as far as I know)

[1] - https://doc.rust-lang.org/rust-by-example/scope/lifetime/sta...

The wording that "'static means different things" is a bit misleading. It means the same thing, but the meaning different because of the context:

Lifetime: lifetime means that the first lifetime outlives (is as long or longer) the second

Type: lifetime means that the type is constrained by the lifetime. Because 'static means "the lifetime of the whole process". That means that the type is not constrained.

I find it very confusing.

T: ‘a is T outlives ‘a; but types are not generated at runtime.

All types are static; which is to say they are created at compile time and exist for the lifetime of the program.

Or does rust generate type variants on the fly at runtime?

I didn’t think it did...

It is a constraint applied to arguments at compile time... but r, that’s what I thought anyway.

A type constraint like T: ‘a or T: Trait is constraining values of type T, not the type T itself. That is T: ‘a is saying “all references in values of type T live at least as long as ‘a”. This includes ‘a = ‘static and the vacuous case of T containing no references.
> T: ‘a is T outlives ‘a; but types are not generated at runtime.

Yes, that’s true, but you can make it a bit more specific by saying “all the references in T outlive ‘a”. Then it should make more sense, as references are generated at runtime.

This gets to the heart of what makes Rust an interesting language, as references are generated at runtime, but how long those references are valid for is checked at compile time using lifetime bounds.

Types are not generated at runtime, lifetimes are types (although they cannot be constructed).

T<'a> and T<'b> are different types altogether (although, being generic, they can resolve to the same type if 'a == 'b, but it's not required). All this happens at compile time.

When you say T: 'a you say that T is a subtype of 'a hence it lives longer.

https://doc.rust-lang.org/nomicon/subtyping.html

Maybe you're being confused by misconception 1 here?

https://github.com/pretzelhammer/rust-blog/blob/master/posts...

Type T, being generic, can be an OwnedStruct, but also an &'a BorrowedStruct or a StructWithReferences<'a>. The owned one is a subtype of 'static since it's owned, but the ones with lifetimes are subtypes of 'a which itself (being a generic lifetime) can be a subtype of 'static or any other lifetime. Again, all this happens and is checked at compile time. This ensures that at runtime, the actual references are alive as per their lifetimes without explicitly checking them anymore.

You misunderstand; what confuses me is how 'static can mean the same thing in both of these contexts.

T: 'static does not mean the references in T must exist for the lifetime of the application.

Ie. the lifetime constraint on T isn’t the same 'static from &'static where the reference must life for the entire lifetime of the application.

Types are static. T: 'static applies to instances at runtime.

These instances do not have and are not related to the 'static lifetime which is “the entire length of the application”.

I accept:

> Type: lifetime means that the type is constrained by the lifetime.

I don't accept the explanation:

> Because 'static means "the lifetime of the whole process". That means that the type is not constrained.

A does not follow from B.

If you want something to mean "A type cannot have references in it" then invent a 'noref lifetime.

'static in this context would mean that all members of T must be 'static, which means that all instances of T must be 'static.

It does not mean that.

I don’t understand why they have the same name; they are not the same thing; it is an example of failure to have orthogonality in the language design in my opinion.

Sorry, I probably edited my comment while you were replying and added a couple links.

Check misconception 2 here, I think it addresses your point.

https://github.com/pretzelhammer/rust-blog/blob/master/posts...

EDIT to your edit:

> 'static in this context would mean that all instances of T must be 'static.

You mean in T: 'static?

No. It means that any instance passed as type T must be bound by 'static and therefore could be held up to the end of 'static. This does not mean that they're allocated at compile time, it just so happens that static variables (allocated at compile time) are 'static but the causality is reversed.

> If you want something to mean "A type cannot have references in it" then invent a 'noref lifetime.

It can have references in it! As long as they're bound by 'static.

Here's an example: https://play.rust-lang.org/?version=stable&mode=debug&editio...

(comment deleted)
Oh, I've read that.

I just maintain that the word 'static is being overloaded here to mean multiple different things.

'static is not the "lifetime of the entire application" when it is used in the context of T: 'static.

> It can have references in it! As long as they're bound by 'static.

:)

...but it can also have values in it which are not 'static.

So is T: 'static, or not?

It's arbitrary semantics; ...but my take on it is:

- IF you take "x is 'static" as meaning the "X is valid for entire lifetime of the application"

then if:

- x: &'static 'is static' and must be valid for the entire lifetime of the application.

I would expect:

- x: T + 'static 'is static' and must be valid for the entire lifetime of the application.

I'm happy to agree that's not what it does mean, what I'm saying is that it is inconsistent for it not to mean that.

> ...but it can also have values in it which are not 'static.

I don't think that's right. It can only have references which live at least as long as 'static does (or longer, but 'static is the longest so...)

I think he means owned values (notice he didn't say "other references") But actually... owned values are bound by 'static!
> 'static is not the "lifetime of the entire application" when it is used in the context of T: 'static.

Yes it is.

T: 'static means T can live up to the end of the "lifetime of the entire application".

> ...but it can also have values in it which are not 'static.

I don't follow. Values are indeed bound by 'static. If they weren't we wouldn't be able to pass values to other threads (which can potentially last as long as our application's main thread).

> 'static is not the "lifetime of the entire application" when it is used in the context of T: 'static.

It is. Any owned instance without non-'static references inside can live up to the "lifetime of the entire application". You might drop them earlier if you wanted to, but you don't have to since it can live up to the "lifetime of the entire application" and therefore can be passed for example into a thread that can hold the value up to the end of the "lifetime of the entire application".

Any owned value can be held indefinitely as long as the program is running.

I'm taking a guess here: you mean that values' lifetimes can be constrained (I guess you mean by dropping the actual value). But it's the owner the one that ended it earlier, not the caller (where 'static applied). You will never be able to have an owned value with a lifetime shorter than 'static without dropping it and, if you drop it, you cannot pass it anywhere. Hence why any owned type that is passed is, by definition, bound by 'static.

> ...but my take on it is:

> - IF you take "x is 'static" as meaning the "X is valid for entire lifetime of the application"

> then if:

> - x: &'static 'is static' and must be valid for the entire lifetime of the application.

> I would expect:

> - x: T + 'static 'is static' and must be valid for the entire lifetime of the application.

Your expectations are correct and that's what it is. Just replace "must be valid" with "can live up to".

That's why you need to pass T: 'static to threads, because a separate thread needs something to hold up to the end of the application since a thread can potentially never end.

https://doc.rust-lang.org/std/thread/fn.spawn.html

Fair I guess; we're just arguing semantics. The point I was originally making is that:

'a: 'static is read "'a outlives 'static" [1]

T: 'static is read "T is bounded by 'static" (apparently, although I can't find a reference to it).

The syntax is the same, the meaning is different.

Maybe the 'static part of these two things is the same, but they seem to me to:

- mean different things

- use the same syntax

It is what it is I guess... just confusing as to why it was decided to use the same syntax for these things, instead of something different.

You could argue that adding new syntax makes the language more complicated; but I'm not sure. Does it make it less complicated when you overload the same syntax with multiple different contextual meanings?

Opinions probably vary.

[1] - https://doc.rust-lang.org/reference/trait-bounds.html#lifeti...

(comment deleted)
What confuses me is... why do you think the meaning is different? There is no meaning overload. It's a single meaning.

T: 'static would happily typecheck with &'a str if 'a: 'static.

T: 'static typechecks:

- OwnedValue

- OwnedValueWithReferences<'a> where 'a: 'static

- &'a ReferencedValue where 'a: 'static /// &'static ReferencedValue

- Foo<&'a Bar> where 'a: 'static /// Foo<&'static Bar>

...and more.

See the example here: https://play.rust-lang.org/?version=stable&mode=debug&editio...

Notice how bar2 has a &'a str where 'a: 'static and it can be happily passed to bar.

Of course in T: 'static you're subtyping a type and in 'a: 'static you're subtyping a lifetime (which implicitly subtypes the type that it's applied on)... but the ": 'static" part means exactly the same: "the left part of this bound can live up to the end of the application". Whether it's a lifetime, a borrowed type or an owned type does not matter.

> It is what it is I guess... just confusing as to why it was decided to use the same syntax for these things, instead of something different.

Because they are the same.

We could of course separate them into 'noref, 'yesrefbutstatic, 'staticref, etc. But then we'd have a needlessly restrictive std:.thread::spawn that would accept only Owned, or only Owned<'static> or only &'static Referenced. The implications are the same, hence they're the same. We wouldn't gain anything and we'd be needlessly restricted.

A simpler way to put it: owned values have an implicit 'static lifetime.

What you say makes sense... but I struggle to reconcile it with reality.

If "the left part of this bound can live up to the end of the application" then why is &a not 'a where 'a: 'static? a can live up to the end of the application. &a can live up to the end of the application.

Why is the result "argument requires that `a` is borrowed for `'static`"

    fn foo<'a: 'static>(a: &'a str) { println!("{}", a) }
    pub fn main() { 
      let a = "hello world".to_string();
      foo(&a);
    }
So I guess I'm going to have to just agree to disagree on this one and bow out of this conversation thread I'm afraid.

[1] -- https://play.rust-lang.org/?version=stable&mode=debug&editio...

What?

If you do 'a: 'static, then you just said 'a is at least as long as 'static.

When you borrow the String, you just created a lifetime that is not as long as 'static. Variables are dropped (and hence unborrowed) in reverse order. So 'a will necessarily be dropped before its owner, hence it's shorter than 'static.

As you can see it complains that it's dropped at the end of main() even though it should be borrowed for 'static. If this was an owned value it would NOT be dropped at main() since it would be moved into the function on call.

Nothing surprising here.

> &a can live up to the end of the application

Nope. Imagine spawning a thread and passing &a but keeping 'a' in your main thread.

> bow out of this conversation thread I'm afraid

Welp I did what I could.

> a can live up to the end of the application. &a can live up to the end of the application.

Nope, a reference to a stack frame can't live up to the end of the application. The stack frame gets deallocated and the referent ceases to exist; therefore the reference you're creating in your linked example doesn't outlive 'static. `main` is not an exception to this in Rust.

> T: 'static does not mean the references in T must exist for the lifetime of the application.

It means that T does not have non static references, so it is not a requirement that it lives for the lifetime of the application but can do so.

You seem to misunderstand what type : lifetime means.

> 'static in this context would mean that all members of T must be 'static, which means that all instances of T must be 'static.

Your "which means that" isn't true. It doesn't mean that. The syntax type : lifetime indicates that the values of the type MUST BE ABLE to outlive the lifetime. It doesn't mean that they NEED to outlive the lifetime.

This means, for example, i32 : 'static even in the case doesn't live the whole 'static lifetime. It COULD live, though, if the author of the code allocated it statically.

> These are totally different things with the same name.

Another way to think about it is that “T: ‘static” restricts all references in T to be static, in the same way that “‘a: ‘static” restricts ‘a to ‘static. The case when T doesn’t contain any references is then just a boring base case.

`T: 'a` means that you may keep values of type T around for lifetime 'a, but not that they will. So `T: 'static` means that you may keep values of that type around forever, as they do not refer to anything that doesn't live forever.

So neither `&'a i32` nor `MutexGuard<'a, i32>` are 'static (unless 'a is), because you shouldn't keep references like that around longer than the stuff it points to. But i32 by itself satisfies i32: 'static, because it's perfectly fine to keep an i32 around forever. (But that doesn't mean that every i32 will stick around forever.)

The missing information here is that lifetimes don't apply at all to types that don't contain any borrowed references (such as `i32`, or self-contained `String`).

So `T: 'static` doesn't require types to live for the entire duration of the program. It requires borrows if there are any to be valid for that long. If there are no borrows involved, then 'static is ignored.

In practice `T: 'static` should be understood as "all temporary references are forbidden here".

Can't take anyone that uses

if(..){

Notation seriously.

In college, I wrote something sort of like this to help students prepping for interviews in C++: https://vghaisas.com/code-quickref/cpp.html

I quite like the format (though this post is much better than mine) of lots of small snippets of code showing how something is used.

Snippets looks quite impressive. But if it's segregated into different post like https://flaviocopes.com/ did, then it'll be much helpful.
>In this article, instead of focusing on one or two concepts, I'll try to go through as many Rust snippets as I can

I really like this. One of the things I like to do when learning a new language is to go to http://www.rosettacode.org/wiki/Rosetta_Code and just go through snippet examples for certain common tasks.

Just quickly going through examples to me feels much easier than trying to learn from principles.

It's a great resource. I like such easy list approach, because many times new language just needs a translation from concepts you already know.

I have a problem understanding how temporaries work in Rust. At least from what I see there is a difference between a temporary inside a vec that I bind somewhere to then pass it somewhere and a temporary inside vec constructed inside method invocation. I asked on SO [0], but did not receive satisfactory answer, or I'm too dense to understand it.

[0] https://stackoverflow.com/questions/64705654/why-i-get-tempo...

This is great!

You probably want to add feeling_lucky: bool as an argument to fair_dice_roll()

This is exactly what I need it <3
Literally the most easiest way to understand Rust.

Just want to take a moment a appreciate your efforts for writing this amazing article. Honestly it made it so easier to understand Rust especially for a beginner like me

This is very good although the title definitely promises too much!
(Side note: dear Apple, please add syntax coloring and code formatting for Rust to Xcode. You have Fortran and C Shell in the list, but not Rust or Go, which are much more popular!)
Is there any reason to use Xcode for Rust development?
It's a habit. Keyboard shortcuts, familiar bugs and glitches, etc are not easy to change.
I was a complete Nube an hour ago... I understand info from Nubes has special value, so I'll be as explicit as I can.

I must be slow, being an old fart... I've been at it for 53 minutes and got about 1/2 way before all the questions in my brain stacked up to "full".

I'd add a recommendation at the top of this to have a Rust compiler handy.

{} were called braces when I learned programming, [] were brackets... this tripped me up, Unicode calls these {} curly brackets ?!? (Why did it trip me up? Because on my screen in non-dark mode, { and [ look identical due to my eyesight, and I assumed I was looking at [ because the text said that's what it was... as you get older, you'll understand)

I don't understand why b=a; c=a; <-- doesn't work because a is "used up"???

I'll get a Rust compiler and start again.

I'm allergic to "macros" as they have had a special place in hell because of their misuse in C... I hope Rust is more sane.

Rust is tricky. Took me about a year to grok decently.

Regarding references, the compiler does flow analysis and tries to enforce a sort of static rwlock semantic on variable level. You either have a single mutable reference xor N readable references to the same variable.

If b is a mutable reference to a, c cannot point to a until b reliquishes the "lock", which happens when b goes out of scope.

>I don't understand why b=a; c=a; <-- doesn't work because a is "used up"???

You know how C has a problem with aliases? (multiple variables referencing the same thing)? Which introduce bugs, prevent optimizations, etc?

Well, Rust tries to prevent this, and make more explicit (and known to the compiler) when you do this...

I've only had a passing acquaintance with C and C++, so I've never used aliases.

I just googled it... why the heck would you copy pointers? That's insane!

In my example, a, b, and c were variables, not pointers.

You keep using this word, variables. I don't think it means what you think it means.

At least in my book pointers are still variables (as in "a pointer variable"), and a variable is any named value, whether it's a scalar or a pointer or a nth-pointer, or what its storage is.

But you mean that in your example there is no way to affect the previous value, right?

>I just googled it... why the heck would you copy pointers? That's insane!

Well, copying values and passing them around would be too costly on memory (for larger structs especially), and would prohibit several techniques.

Variables can be varied... you can increment, decrement, do whatever you want with them. They keep track of things.

Pointers are used reference things allocated from the heap, and never anything else, unless you're insane. Pointers get directly handled in linked lists, trees, etc.

If at all possible, pointers should be avoided otherwise.

Values passed to a procedure can be done by value (the default in Pascal), or by reference (VAR parameters).

I don't see why anyone wouldn't copy values by default... it is the only sane way to do things.

> Variables can be varied... you can increment, decrement, do whatever you want with them.

Variables by themselves are not much. They inherit the properties of the type they are bound to. So what you can do with them can be as restrictive or as permissive as the type allows. That type can be a pointer/reference as well.

> Pointers are used reference things allocated from the heap, and never anything else, unless you're insane.

People routinely use pointers to things on the stack in several languages. Rust even makes it safe to do that, using lifetimes - a pointer to something on the stack can't outlive the stack frame it points into.

> I don't see why anyone wouldn't copy values by default...

Because not everything is safe to copy. In particular, Rust has a few kinds of pointers which come with special rules.

Firstly, it has boxes, which always point to something on the heap, and have a rule that that when the pointer dies, the thing it points to gets freed. If you copied a box, then when one of the copies died, the thing would be freed, and then the other copy would have a pointer to invalid memory, which would be bad.

Secondly, it has mutable references, which come with a guarantee that a mutable reference is the only pointer to a given thing. If you copied a mutable reference, you would break that guarantee.

Thirdly, it has reference-counting pointers (these are in the standard library, not the language). You can make duplicates of those, but they have to increment their reference count when you do so. Copying is always just a bitwise copy, so there is no chance to increment the reference count. Instead, duplication is an explicit operation.

There are a few other things it doesn't make sense to copy. Like, what would it mean to copy a mutex?

So, in Rust, you can't copy by default. However, it is really easy to mark a type as being copyable (the compiler will check that it really is, ie doesn't contain any non-copyable things), and then you can copy it.

>Variables can be varied... you can increment, decrement, do whatever you want with them. They keep track of things.

You can do the same to a pointer in languages that have them, either directly (e.g. in C) or through some "unsafe" construct (e.g. in Rust, C#, Go).

>I don't see why anyone wouldn't copy values by default... it is the only sane way to do things.

When resources are ample, yes. Not the case historically, or in many use cases today.

And not all values make sense to copy.

But also in Rust, we're talking in the context of C performance needs, memory models, and concepts, and Rust expands and makes those safe.

> why the heck would you copy pointers?

Pointer aliasing is not always obvious.

It's definitely a lot to take in, you have the right idea — the compiler is here to help, the diagnostics are wonderful and improving every week thanks to the work of Esteban Kuber and others.

Re macros: try to keep an open mind if you can, C macros and Rust macros are completely different. You can get very far without reaching for them, so don't worry too much!

"I don't understand why b=a; c=a; <-- doesn't work because a is "used up"???"

Had the same issue a few months ago.

The pointer can only be referenced by one variable at a time.

If you "move" the pointer from a to b then a is figuratively "used up" because a is now blocked from doing anything with the pointer anymore.

I guess, for Rust itself the pointer is still referenced by a, Rust just blocks you from using it. But thinking you moved it to another variable helped me a bit.

if I say

a = 2; b = a; c = a;

There's no pointer in there, just 2.

That will work fine, because 2 is 'Copy', so it can be copied trivially (as are structs consisting only of Copy members which are marked as Copy). Roughly speaking anything with a pointer in it isn't Copy, though many objects will implement Clone, which basically just means you need to explicitly call .clone() to make a copy instead of it happening implicitly.
2 is Copy? It's an integer, could be 2.0 if you wanted to make it a float.

Someone needs to explicitly nail down the mission statement of Rust... because to me it seems to be

"Rust will introduce pointers where they don't need to be, and then try to protect you from the results with obsessive rules"

You need to seriously read more on Rust before making judgments. You're clearly confused.

What '2 is Copy' means precisely that Rust WILL NOT 'introduce pointers where they don't need to be' - 2 is fine to 'alias' because it's a primitive type and so a copy is made when you do that, (i.e. the type implements the 'Copy' trait). However when you do that with complex types that do not implement Copy, you're moving ownership to the new variable so cannot use the old one any more.

Regarding a getting used up, the Rust compiler enforces a single-ownership principle where all values must have a single owner. If you move ownership of a to b, you cannot use a anymore as it no longer has ownership of the value.

The only exception to the above is if a type "is Copy". This means that values of this type can be copied very cheaply, and in this case the compiler will allow you to use it multiple times, which is implemented by it being duplicated on each use.

As for macros, they are nothing like C macros.

> I'd add a recommendation at the top of this to have a Rust compiler handy.

If you haven't already, check out the Playground: https://play.rust-lang.org/

It's reasonably full-featured for a web IDE (much more so than the Go playground), and it includes many commonly used packages.

> Because on my screen in non-dark mode...

The little sun icon in the lower left corner of the page turns on dark mode! Hopefully that helps.

> I don't understand why b=a; c=a; <-- doesn't work because a is "used up"???

This is something called "linear typing", and it's admittedly pretty unusual in a mainstream language.

The core idea is that the assignment operator _only ever_ creates a "shallow" (bitwise) copy of data; it never invokes anything like C++'s copy assignment operator. For types that are "plain old data" (like primitives), the old and the new values are fully independent, so the assignment works the same way it would in most languages, i.e., `a` is not "used up". This is what other commentors mean when they say that primitives "implement `Copy`". But if the old value has pointers or references, then the two values are not independent: after the bitwise copy, they both have pointers to the same data. Since data can only ever be shared explicitly in Rust, and the assignment operator never performs a deep copy, the old value, `a`, is considered invalid and cannot be re-used.

If you're familiar with C++11 or later, one way to think of it is that `=` in Rust always behaves somewhat like `std::move` in C++:

`b = std::move(a);`

The details are substantially different (this will call `a`'s move-assignment operator if one exists, which has no equivalent in Rust, and C++ offers no support for ensuring that `a` is no longer used if its move-assignment operator invalidates it). But the general idea that "move semantics are on by default" is essentially accurate.