There two inaccuracies at the beginning. First, it's not that "all primitives are stored on the stack". It's that `let` variables are stored on the stack. It doesn't matter if they contain an instance of a primitive type or any other type. A let binding is placed on the stack, just like with probably any moderately modern language like C, C++, Swift (in the absence of mutable captures), and even Pascal's descendants. What happens is that the core language itself doesn't provide dynamic allocation facilities; if you want that, you have to use an explicit pointer to explicitly heap-allocated memory. That's what Box (and other smart pointers) do, wrapping the raw pointer (which, if it's in a let binding, can still be on the stack) safely and handling memory management using RAII. The second mistake is related. The "recursive type" definition is not issued because "the list is stored on the stack". Whether the list's element and the next/tail pointers are stored on the stack or heap is irrelevant. What matters is that the `enum List { Const(i32, List), End }` type contains itself, so it would have an infinite type. What Box does in this case is it inserts indirection, because it's a pointer. Of course, it also imposes heap allocation, but that doesn't matter. The link list would work just as well with a definition like `enum List), End }` - the essence is that there has to be indirection in a recursive type. In this latter case, though, a list node would only own its data, but it would not own / automatically deallocate the next and subsequent nodes, because primitive references only provide indirection, not memory management, unlike Box.
Yeah, I am aware of these mistakes. All of these videos are live coded without any kind of script. Of course, I make mistakes; and because of that I wish that TH-cam provided the annotation engine that they used to have (which they don't) so that I could write in the fixes. Also, this is part of the reason why I haven't done anymore basic tutorial intros to languages since my Kotlin Tutorial series (where I made way too many mistakes). The 2nd error that you pointed out was actually an intentional error. I chose to simplify the explanation of the Box pointer type as an item that just stores data on the heap rather then the stack (I just wanted to make people aware of this datatype). The cons example was mainly just to show off this particular attribute; but the cons list itself was not a main focus in this tutorial. And yes you are absolutely right about using indirection to create the recursive type; the stack and the heap in this case are more or less irrelevant. I also chose to not really explain the stack and the heap all that well in this tutorial series overall. I mainly just mentioned that the stack is for quick access with LIFO and the heap is more general. There is a lot more that I could have done in this regard but I chose not because I didn't want to overwhelm the users with this information. For most people who just want to learn Rust for programming, a general explanation of the stack and the heap should be enough; since I doubt many of them are going to be creating compilers or Operating Systems. Anyhow, thank you for pointing these out and giving such detailed explanations.
Glad my rambling made sense. Move essentially lets you capture the environment by value. In other words, it owns the values which are used in the closure which prevents it from causing race conditions and stuff like that.
Nice, but please use easier examples in the future. Why all these generic/traits for explaining closures? It would be easier to just focus on one thing at a time. Also please use real life examples. You could use struct Person instead of just struct A or similar. But besides that your videos are great. Sorry for being a noob.
Using Generics because I am talking about Generic functions along with Closures. Its a part of the topic when it comes to Rust. The Rust beginner series is just to go through the basic concepts in isolation without applying them to real world examples. As an aside, I do agree with you though, I could have approached these concepts a bit better overall. As you go further, you'll find the actual projects which are real world examples.
You could, the closure would be like any other data type. For instance in some of my projects, I've used functions that look a bit like this: Fn(Type1, Type2) -> ReturnType as arguments to other named functions. And in those cases, it exposes the closure state into whatever you use it with. I have an example of this in stronghold and it tauri. With Tauri its for the event manager (basically resolves the JavaScript futures) and then with Stronghold, its to exposes a Database View and a Buffer.
@@TensorProgramming thanks for the confirmation. I resolved my issue after learning about a few threading primitives like Arc and Mutex. The closure I was writing is now thread-safe, etc. This language feels like the future, eager to try more.
`for n in &arr {}` equal to `for n in arr.iter() {}`. Learned today. But someone said: The slice type &[T] does implement IntoIterator. Which one dose &[T] implemented, into_iter() or iter() ? into_iter() consumes and moves elements.
If we are just talking about slices than both are implemented indirectly I believe. Slices get two structs associated with them, iter and iter_mut and both contain implementations of the two traits you are talking about (So you have to convert the slice into an interator first before you can use either unlike with a Vector). Both are also implemented on the Vector type. You can find this kind of information in the documentation: doc.rust-lang.org/std/slice/index.html Notice, slice.iter() would create an Iter struct which contains a blanket implementation of IntoIterator and a trait implementation of Iterator. IterMut also contains both and can be created using let mut _ = slice.iter();
@@TensorProgramming Thanks for the quick reply. Lot's of new concept I just learned in last few weeks. It'll take me some time to digest all aspect of the iter when I work more with them.
@@TensorProgramming That's what I thought too. You're much better than stackoverflow. ^_^ I started learning Rust one month ago to prepare to join an open source project.
There two inaccuracies at the beginning. First, it's not that "all primitives are stored on the stack". It's that `let` variables are stored on the stack. It doesn't matter if they contain an instance of a primitive type or any other type. A let binding is placed on the stack, just like with probably any moderately modern language like C, C++, Swift (in the absence of mutable captures), and even Pascal's descendants. What happens is that the core language itself doesn't provide dynamic allocation facilities; if you want that, you have to use an explicit pointer to explicitly heap-allocated memory. That's what Box (and other smart pointers) do, wrapping the raw pointer (which, if it's in a let binding, can still be on the stack) safely and handling memory management using RAII.
The second mistake is related. The "recursive type" definition is not issued because "the list is stored on the stack". Whether the list's element and the next/tail pointers are stored on the stack or heap is irrelevant. What matters is that the `enum List { Const(i32, List), End }` type contains itself, so it would have an infinite type. What Box does in this case is it inserts indirection, because it's a pointer. Of course, it also imposes heap allocation, but that doesn't matter. The link list would work just as well with a definition like `enum List), End }` - the essence is that there has to be indirection in a recursive type. In this latter case, though, a list node would only own its data, but it would not own / automatically deallocate the next and subsequent nodes, because primitive references only provide indirection, not memory management, unlike Box.
Yeah, I am aware of these mistakes. All of these videos are live coded without any kind of script. Of course, I make mistakes; and because of that I wish that TH-cam provided the annotation engine that they used to have (which they don't) so that I could write in the fixes. Also, this is part of the reason why I haven't done anymore basic tutorial intros to languages since my Kotlin Tutorial series (where I made way too many mistakes).
The 2nd error that you pointed out was actually an intentional error. I chose to simplify the explanation of the Box pointer type as an item that just stores data on the heap rather then the stack (I just wanted to make people aware of this datatype). The cons example was mainly just to show off this particular attribute; but the cons list itself was not a main focus in this tutorial. And yes you are absolutely right about using indirection to create the recursive type; the stack and the heap in this case are more or less irrelevant.
I also chose to not really explain the stack and the heap all that well in this tutorial series overall. I mainly just mentioned that the stack is for quick access with LIFO and the heap is more general. There is a lot more that I could have done in this regard but I chose not because I didn't want to overwhelm the users with this information. For most people who just want to learn Rust for programming, a general explanation of the stack and the heap should be enough; since I doubt many of them are going to be creating compilers or Operating Systems.
Anyhow, thank you for pointing these out and giving such detailed explanations.
I realize it is kind of off topic but does anyone know a good place to stream new movies online?
Thank you for this great writeup, It clears things up a lot about Box.
This is so well done. Thanks you!
Glad you found it useful.
remarkably detailed. thanks!!!
I am glad you enjoyed it.
Cool video, thanks!
9:21 this is the best description of why I’ve seen “move || ….” All over the place like in std::thread
Glad my rambling made sense. Move essentially lets you capture the environment by value. In other words, it owns the values which are used in the closure which prevents it from causing race conditions and stuff like that.
Nice videos man. Thanks.
Hmm curious why there’s nothing similar to Go’s escape analysis to move values onto the heap, instead of using that move keyword
Rust has very little runtime and of course this also means that it doesn't have a GC.
8:50 is seems like in current Rust version this needs to be Box
Yes, that would be correct. The current Trait system requires dynamic traits. This is still valid syntax though as far as I know.
Nice, but please use easier examples in the future. Why all these generic/traits for explaining closures? It would be easier to just focus on one thing at a time. Also please use real life examples. You could use struct Person instead of just struct A or similar. But besides that your videos are great. Sorry for being a noob.
Using Generics because I am talking about Generic functions along with Closures. Its a part of the topic when it comes to Rust. The Rust beginner series is just to go through the basic concepts in isolation without applying them to real world examples. As an aside, I do agree with you though, I could have approached these concepts a bit better overall. As you go further, you'll find the actual projects which are real world examples.
@@TensorProgramming Sweet, I will definitely be checking those out! Thanks man!
Not a problem, If you have anymore questions please feel free to let me know.
Is it possible to return a closure that presents hidden state from the module it was defined in?
You could, the closure would be like any other data type. For instance in some of my projects, I've used functions that look a bit like this: Fn(Type1, Type2) -> ReturnType as arguments to other named functions. And in those cases, it exposes the closure state into whatever you use it with. I have an example of this in stronghold and it tauri. With Tauri its for the event manager (basically resolves the JavaScript futures) and then with Stronghold, its to exposes a Database View and a Buffer.
@@TensorProgramming thanks for the confirmation. I resolved my issue after learning about a few threading primitives like Arc and Mutex. The closure I was writing is now thread-safe, etc. This language feels like the future, eager to try more.
@@AvindraGoolcharan Parts of the language feel futuristic for sure. Glad you were able to figure out on your own what you wanted.
why does the "fn" macro not require "!"???
fn is not a macro, its a keyword.
`for n in &arr {}` equal to `for n in arr.iter() {}`. Learned today.
But someone said: The slice type &[T] does implement IntoIterator.
Which one dose &[T] implemented, into_iter() or iter() ? into_iter() consumes and moves elements.
If we are just talking about slices than both are implemented indirectly I believe. Slices get two structs associated with them, iter and iter_mut and both contain implementations of the two traits you are talking about (So you have to convert the slice into an interator first before you can use either unlike with a Vector). Both are also implemented on the Vector type. You can find this kind of information in the documentation: doc.rust-lang.org/std/slice/index.html
Notice, slice.iter() would create an Iter struct which contains a blanket implementation of IntoIterator and a trait implementation of Iterator. IterMut also contains both and can be created using let mut _ = slice.iter();
@@TensorProgramming Thanks for the quick reply. Lot's of new concept I just learned in last few weeks. It'll take me some time to digest all aspect of the iter when I work more with them.
@@goodwish1543 Right on, good luck and if you have anymore queries feel free to ask.
@@TensorProgramming That's what I thought too. You're much better than stackoverflow. ^_^
I started learning Rust one month ago to prepare to join an open source project.
@@goodwish1543 Ahh, I wish you luck then.
rust, the video game?
Nope, the programming language.