2
you are viewing a single comment's thread
view the rest of the comments
view the rest of the comments
this post was submitted on 07 Feb 2024
2 points (100.0% liked)
Learning Rust and Lemmy
460 readers
7 users here now
Welcome
A collaborative space for people to work together on learning Rust, learning about the Lemmy code base, discussing whatever confusions or difficulties we're having in these endeavours, and solving problems, including, hopefully, some contributions back to the Lemmy code base.
Rules TL;DR: Be nice, constructive, and focus on learning and working together on understanding Rust and Lemmy.
Running Projects
- Rust for Lemmings Reading Club (portal)
- Rust beginners challenges (portal)
- Heroically Helpful Comments
Policies and Purposes
- This is a place to learn and work together.
- Questions and curiosity is welcome and encouraged.
- This isn't a technical support community. Those with technical knowledge and experienced aren't obliged to help, though such is very welcome. This is closer to a library of study groups than stackoverflow. Though, forming a repository of useful information would be a good side effect.
- This isn't an issue tracker for Lemmy (or Rust) or a place for suggestions. Instead, it's where the nature of an issue, what possible solutions might exist and how they could be or were implemented can be discussed, or, where the means by which a particular suggestion could be implemented is discussed.
See also:
Rules
- Lemmy.ml rule 2 applies strongly: "Be respectful, even when disagreeing. Everyone should feel welcome" (see Dessalines's post). This is a constructive space.
- Don't demean, intimidate or do anything that isn't constructive and encouraging to anyone trying to learn or understand. People should feel free to ask questions, be curious, and fill their gaps knowledge and understanding.
- Posts and comments should be (more or less) within scope (on which see Policies and Purposes above).
- See the Lemmy Code of Conduct
- Where applicable, rules should be interpreted in light of the Policies and Purposes.
Relevant links and Related Communities
- Lemmy Organisation on GitHub
- Lemmy Documentation
- General Lemmy Discussion Community
- Lemmy Support Community
- Rust Community on lemmy.ml
- Rust Community on programming.dev
Thumbnail and banner generated by ChatGPT.
founded 2 years ago
MODERATORS
Even though The Book is a bit verbose in these first few sections and really only touches on the basics of the language, I enjoyed going through it! Once you've gone to the end of just chapter 2, you've touched on project management with
cargo, compiling withrustcorcargo,matchstatements, and some of the other syntactical details of the language. It's definitely enough to get you started if you're new to the language!For me, my outstanding questions, which come from the final exercise that builds a "guessing game" (code extracted below for easy reference):
Orderingactually is and what mechanically happened when it was used in the match statement.rand::Rngbut then writerand.thread_rng().gen_range(). That I'm importing something not explicitly used in the code (Rngis never used) feels off to me. I can only guess that this was a shortcut to get to use a higher level interface. But what isRng?&secret_numberand&secret_number. Given rust's concern with ownership and the "borrow checker" as a means of preventing memory safety issues ... why isn't it the default behaviour that a variable is passed by reference? Why do we have to explicitly pass the reference ourselves (with the ampersand syntax&secret_number)?This is just my attempt at answering (I'm learning too):
Macros are easy to use, allowing beginners to write 'hello world' for example, but hide a bunch of complicated stuff we're unlikely to understand until later. (I vaguely remember from Java that just writing something to the console was a whole bunch statements strung together.)
I found it useful to document what each line was doing in the example, to get my head around the terminology.
stdis a crate,iois a module in thestdcrate, and that module provides methods likestdin()stdis a crate,cmpis a module in thestdcrate, and that module provides enums likeOrderingrandis a crate,Rngis a trait from therandcrate, and that trait provides methods likethread_rng()Orderingis a enum - a type with a list of variants with developer-friendly names. In the same way that a 'DaysOfTheWeek' enum would have 'Monday', 'Tuesday' ..., etc,Orderinghas Less, Greater, or Equal.matchstatements work with enums, in a 'for this, do that' kind of way.Rngis a trait, which feature a lot in Rust, but is something I've had difficulty getting my head around. Where I'm at so far is they mostly provide convenience. Therandcreate provides a number of structures in itsrngsmodule -ThreadRng,OsRng,SmallRng, andStdRng, depending on what you want to use to generate randomness. Instead of sayingThreadRngprovides thegen_range()method, andOsRngprovides thegen_range()method, etc, Rust just says they implement theRngtrait. SinceRngprovides thegen_range()method, it means that everything that implements it will provide it.thread_rng()returns aThreadRngstructure, which providesgen_range()via theRngtrait, but we need to bring that trait into scope with theusekeyword in order to be able to call it.For the default behaviour of passing owned vs. borrowed variables, I guess it's useful to explicitly state "I'm giving this variable to another function, because I don't intend to use it anymore", so that if you inadvertently do, the compiler can catch it and error.
Thanks!!
This makes a lot of sense actually. Thanks! It does lead to the awkward situation where you'd have to know that Rng is the underlying trait of the rest of the module and so import it. But like I said, I'm guessing that's because this example is aiming only for easy high-level usage of
rand. I'd guess there's a lower level way of using therandcrate that would involve more direct imports, perhapsuse rand::ThreadRng?Makes sense.
Sooo ... is passing by value a thing in rust? Or does just about every method take only reference types as arguments?
I think this is an occasion where a vague familiarity with other languages ended up confusing me with Rust. The '&' sign doesn't mean 'pass by reference' in the same way as it does in C. Anything with a size that's fixed at compile time is typically passed by value, whereas variables who's size might change are passed by reference. The '&' in Rust isn't about that. For variables that are passed by reference, the '&' is about whether the ownership of that memory address is transferred or not.
To illustrate:
works fine as it is, but will error if you uncomment the second println! The 'v' variable was passed by reference, but it's ownership was transferred, so it can't be referred to again.
Are you sure? I noticed the rust book said this:
No, I'm not sure, tbh. It's a concept I'm struggling with, and I reliant on others to correct/question me. I was trying to answer the question of whether things get passed by value or not, and I wanted to say yeah, loads of things do (anything who's size is known at compile-time) and to caution against thinking too much in C terms.
Here's where my thinking is now: a variable without a & in front is passed by value. A primitive type (i.e. something who's size is known) will be copied. So if a=4 and you pass it to a function, you can still refer to a later. A variable-length type (e.g. a String) can't be copied, so it is moved, and referring to it later will be an error.
A variable with a & in front is indeed a reference. It's a memory address, so it's of fixed size. For either a primitive or a variable-length type, the address can be copied when passed to a function, so it can be referred to again later without issue.
This feels more correct to me, so hopefully it is. If not, I'm sure someone will have a better answer soon (this community is growing well!).
On the whole primitive types and references thing, I find it helps me to remember that a reference/pointer (subtle but importance difference between rust and C where rust has more guarantees around a pointer to make it a "reference) is also basically just a number like a "primitive" i32 etc. And references/pointers obviously (?) have to get passed by value or copied (in order to "survive" their original stack frame right?), so passing any primitive by value or copying it really isn't different from passing it by reference, apart from when you're running a borrow checker for managing memory of course.
This will make more sense once we (this community) get to the 4th week/session and properly get into ownership, but lemme try to explain anyways:
A variable without a
&in front is moved into the [function's] scope.Upon exiting a scope, Rust automatically drops/de-allocates any variables that were owned by/moved into said scope.
This is why you need to either
pass by ref / have the scope borrow a reference to the variable, or
have your function return the variable/object/memory handle that was moved into it
when you want a variable/some data to "out-live" being passed as argument to a function call.
Most often you will use 1), but there are some cases where it can be much nicer to move things "into" a function call and store what you "get back out" (i.e. the return value). Using a "[Type-]State" pattern/approach is a good example of such a case (here's a better explanation than I can give in a lemmy comment).
Example:
Here, we as programmers don't need to worry about making sure
un_authed_usergets cleaned up before the program exits, and we don't need to worry about data that could have been stored insideun_authed_userbeing freed too early (and thus not being available toauthed_user).Admittedly, this is a contrived example that doesn't really need to worry about ownership, it's just the bare minimum to illustrate the idea of moving data into and then back out of a function scope. I don't know of a small enough "real-world" example to give here instead.
Thank you.
I think there's more to it though, in that simple values aren't moved, they're always copied (with any
&in front indicating whether it's the value to copy or the address)To illustrate:
The 'a' was copied, and the 'fruit' was moved.
Correct! Values that can be copied are copied, and those that aren't are moved.
All primitive data types are copyable (or "are
Copy", which will make more sense once we get to Traits). I think arrays of primitives are as well? Most everything else isn't by default.One of my favorite parts of Rust is that to make something copyable, you "just" implement the
Copytrait for that thing.