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.
An alternative introduction to Rust
71–80 of 94 posts
Re: An alternative introduction to Rust
#72Earlier quoted context omitted.
A very short TLDR, in my limited opinion, is basically "Take an ML-ish, make sure everything has C-like performance, while prohibiting all memory safety issues and eliminating aliasing". From those basic principles things you can start reasoning a lot about what Rust must do. But I agree it does feel a bit weird to need to explain pointers while also assuming people understand memory layouts. There should probably be…
Note that Rust doesn't eliminate aliasing, it merely tracks it very precisely. Taking a reference is a trivial way to create an alias, though obviously while the alias is alive you are restricted in what you can do to the referent.
When you enforce immutability of aliases, as Rust does, aliasing stops being a special case. The underlying value may be the same, but the behaviour is identical to when they are independent.
The only time you have problems thereafter is when you do weird things with the value's address which only coincidentally works. For example, comparing interned strings or Java's Integer objects using == works for other interned strings and small integers, respectively.
Re: An alternative introduction to Rust
#73Earlier quoted context omitted.
Regarding the first point, about scope, I'd guess he's referring to lifetimes and how you can explicitly annotate that something can live as long as another thing, like a parameter. Not scope exactly.
Right. When you give an object A a lifetime that makes it live as long as object B, you're effectively putting object A into the same scope, for memory allocation purposes, as object B. This works even if B is in an outer scope. This is a new, and very powerful concept. Any tutorial on this needs to be illustrated. Talking about line numbers is not enough.
As I said above, there's no lifetime annotations you can use to ensure that y lives as long(aka is in the same scope) as x in the following
let x = 1;
{
let y = 2;
}
This is also true if you do dynamic allocations: there's fundamentally no way to "promote" variables to a higher scopes just by using lifetime annotations.Lifetimes are a purely passive thing, designed to allow the compiler to check the programmer's use of references, not to allow the compiler to massage things around to ensure that whatever use work.
That is, if a Rust program compiles, then it will also compile (modulo needing extra `unsafe` annotation) and, importantly, do the same thing, if all the references are replaced with raw pointers, which don't have lifetimes to describe their relationships.
Re: An alternative introduction to Rust
#74Earlier quoted context omitted.
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 li…
Regarding the first point, about scope, I'd guess he's referring to lifetimes and how you can explicitly annotate that something can live as long as another thing, like a parameter. Not scope exactly.
Re: An alternative introduction to Rust
#75Earlier quoted context omitted.
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 li…
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 where P: std::ops::Neg + 'a, ::Output: 'a { foo: &'a Bar , } after further research this makes sense, of course, but looks like non-sense to anyone just trying to learn the language
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`. :)
Re: An alternative introduction to Rust
#76I 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…
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 :/
Re: An alternative introduction to Rust
#77I 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 fi…
I would then go further than you suggest, saying something like, "The blank lines, 1,3 and 7, do not have any impact on the program." Then "Line 4 binds the value 5 to the variable x.", followed by the explanation of what that is called in rust.
After that, "Lines 2 and 6 are the beginning and end of the program. For this program, they also define the scope that owns 'x'." Followed by the explanation of ownership.
Once 'let' is established, a short explanation of 'mut' can follow.
Re: An alternative introduction to Rust
#78Re: An alternative introduction to Rust
#79Earlier quoted context omitted.
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 where P: std::ops::Neg + 'a, ::Output: 'a { foo: &'a Bar , } after further research this makes sense, of course, but looks like non-sense to anyone just trying to learn the language
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 a…
Re: An alternative introduction to Rust
#80It'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 indicat…
Thanks, this explanation is about 10-100 times better than the one in the submitted article.