this is the best rust content produced, ever. Most rust devs or wannabes have programmed a computer before so will quickly get tired of "from zero" tutorials
it puts me on edge when I hear "so we could do it like this..." in a programming tutorial. I then have no idea if it's a valid alternative or just plain wrong until I watch the rest of the video
I am so, so, so tired of writing javascript and python. The most complicated thing I do all day is refactor crapping javascript formatting and try to convince people to follow better eslint rules, learn yet another annoying new npm package that probably has way to many dependencies, or have to constantly keep CTRL-F 'ing through a crappy python function that is 100 lines long. I am ready for Rust. Let's go!! Great video. Great explanation. Thank you so much 🙏
Thank you Jon for all these learning materials. This is definitely helpful. I'm going through the whole crust of rust series and then I'll go through your book as well. I already book the pre-release. 💪😁
I am learning so much from you. Working with rust code from your streams and going from (kind of getting it) to (okay I actually GET it) feels oddly satisfying, and I have never felt like this while learning other languages over the years.
Really enjoying these Crust of Rust videos. The more lengthy videos are interesting, but I often feel lost halfway through. The shorter ones feel more relevant when you're still learning the basics but have gotten through the book.
10:00 into_iter takes the ownership of the value, which is the meaning of "into", the ownership is transfered "into" the iterator. value.into_iter() makes sense this way :)
Yup, that's all correct. Keep in mind as I show later on though that `iter` is an inherent method, while `into_iter` is a trait method from the `IntoIterator` trait. Which means that other types and methods can be generic over it. This is why you'll still want to implement `IntoIterator` for, say, `&Vec` (which would effectively do the same as `Vec::iter`)
4 ปีที่แล้ว +7
> into_iter takes the ownership of the value, which is the meaning of "into", the ownership is transfered "into" the iterator True, though I want to point out something that had me confused for a long time, in case it helps somebody else: `into_iter` taking ownership initially led me to think that it always consumes the underlying collection. That's not necessarily the case though, because that "value" can be a reference, in which case `into_iter` takes ownership of a reference (a.k.a. precious little). E.g. there's an impl of `IntoIterator` for `&Vec`: doc.rust-lang.org/src/alloc/vec.rs.html#1971-1978 (which basically just defers to `Vec::iter`, as Jon says). Here's a playground with a bit of code that helped me clear up that confusion: play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4a6c2e0230d98237e7ad86d239db4655 (I'm only part way through watching the video -- which is great as always, thanks! -- so apologies if this is addressed somewhere in there :) Still, hopefully someone might find it useful to have this information in the comments as well.)
Thank you for the video! I have used iterators in other languages such as java and I am fairly familiar with the concept. But I still found your explanation useful.
I'm leaning rust but... but one things I've started to notice (especially in this video ) is how crucial testing is and how rust makes it easy to encourage you to write them...
true, I recently have started to explore his playlists. he is next level programmer. He is bringing so much value , low level understanding of iterator, macros and other stuffs which we can use to rewrite the industry level framework stacks from scratch in rust itself. although i am keeping the hype aside. but the core rust is immense valuable and future .
It's so great about how to think and try to implement by self to learn. By the way, is there any vim/nvim config in the video? It is good to have such lint related info or error show up. Thanks
Hey, can you put together a stream for the topic Smart pointers? I personally read the book about 2-3 times and still don't get the Cell and RefCell pointers.
It would be cool to upload the streams as full-fledged YT streams such that the chat is also recorded and we can see it after the fact, but I am not sure if it is possible to cut the intro bootstrap part...
I don't know if it's a conscious choice or you don't know about it, but coc offers rename refactoring with (coc-rename) I noticed you do a lot of manual renaming, so I thought I'd point it out. I've personally got it mapped to F3
They are already enabled, but TH-cam unfortunately isn't great about adding automatic captions for longer videos. Community captions are enabled though if someone feels a calling!
I think the reason that `O::item::item` could not compile is because O::item could be associated constant. This is same with C++, where you need to use `typename` to tell the compiler that `item` is a type not a value.
9:58 iter and into_iter are 2 different things, aren't they? Iter is "read-only access" and into_iter is "consuming access" In what way do they do same thing?
I think the idea is that a.iter() is effectively just an alias for &a.into_iter(), and a.iter_mut() is also effectively just an alias for &mut a.into_iter(). into_iter() will give "read-only access", "mutating access", or "consuming access" depending on the context. You could also think of it as consuming the references when called with a reference. iter() and iter_mut() are redundant but convenient.
I'm still about halfway through the video (44:50), and it seems we finished talking about flatten, but can't it be improved? I don't see why it would need an Option for the inner iterator. We could have new like this: ``` let inner = iter.next(); Flatten { outer: iter, inner } ``` and next like this: ``` loop { if let Some(elem) = self.inner.next() return Some(elem); self.inner = self.outer.next()?; } ``` This seems more elegant than the implementation of next that's shown in the video.
I can see that this wouldn't work for DoubleEndedIterator. We would need to call next_back in new, and so our Flatten wouldn't accept iterators that don't implement DoubleEndedIterator. Although it seems to me that Flatten and DoubleEndedFlatten should have been two types anyways, given that they don't need the same fields... Maybe in practice it's better to have one type for both use cases, but I'm not experienced enough to judge when that might be the case.
I find it interesting that they choose to use & when denoting a parameter must be borrowed (ref), but for patterns they use the 'ref' keyword. That tripped me up when I was first learning rust. Any thoughts on why they chose to spell it out in patterns (maybe just to indicate it's a pattern) ?
The two are actually not the same. They're more like opposites. A &foo in a pattern is "match foo as what's behind the reference", whereas ref foo is "match foo as a reference".
I touch on this towards the end of the video, and why it currently has to be there. You're right that it's wasting a bit of memory, but keep in mind that it's just storing an extra iterator, which is itself unlikely to be very large (a hash map iterator for example is mostly just a couple of pointers). In practice, it's also unlikely to matter much, since it is stack allocated, and iterators are usually short-lived. It is unlikely that you have, say, hundreds of thousands of `Flatten` iterators active at the same time.
I feel like every topic you introduced you just said "but this isnt what we're here to talk about" or "but that isn't important right now" and it left me wondering what IS being demonstrated
I'm not sure if this gets answered later on or if I missed something, but regarding the Flatten implementation: is there a particular reason why O is bounded to Iterator, but O::Item is bounded to into Iterator? why not the other way round or have them both be the same?
I don't know if this became clearer throughout the stream, but the answer is that the outer iterator has to be Iterator because we store the outer iterator itself directly in the struct (if it was IntoIterator, and we stored a O::IntoIter, I _think_ the compiler would complain). We also don't _need_ the Into part of IntoIterator in Flatten, so why make the bound larger than it needs to be? For the inner iterator however, we _are_ going to turn each element of the outer iterator _into_ an Iterator, so we need it to implement IntoIterator. If it just implemented Iterator, then we'd need to mutate the iterator we're walking over directly!
It works fine because the inner iterator also keeps track of the fact that it's been exhausted. So next time around, we'll enter the outer `if let`, but the call to `next()` will yield `None`, and so we'll fall down to the code below again. But that means we're now relying on it being okay to call `next()` on the inner iterator after it has returned None, which is not the case for all iterators!
Not sure I understand the purpose of associated types and why not use generics. If all they're for is as you said, to make life easier for the compiler and maybe shave off a second of compile time, why not just use generics and be done with it? surely there's more to it then that? I can't imagine the core team introduced this language "construct" just for that? Does it provide additional benefits?
As I alluded to in the video, they enable you to constrain implementation of the trait to only a single instance per type, which can often times be necessary to get type inference to work. Associated types also tend to be more ergonomic to use, precisely because they're more constrained. Take the Future trait for example - you don't need to specify the Output type every time you take a F: Future, because the Output type is associated, and thus dictated by F.
Can anyone say if an Associated Type basically just a typedef nested into a type, or is there more to it than that? Sounded like a new concept or something when first learning rust, and some compare to generics, but it's hard-coded to some type (which may or may not be a generic type). But a direct comparison to generics doesn't really seem to make sense to me at all - even if the type being defined leverages generics. Associated type is just a nested typedef, or I am actually way off here?
Hi Jeff! An associated type is sort of like a nested typedef, but it is nested not per type, but per implementation of a trait. For example, if some type Foo implements the trait Bar twice for two different Ts, it can use different types for an associated type of Bar for those two impls. Associated types can also have trait bounds, and can be referenced elsewhere in the trait. For example, the Deref trait from the standard library has an associated type Target, and a method fn deref(&self) -> &Self::Target.
I don’t think your explanation of .iter vs .into_iter is correct. .into_iter is a trait method that consumes the value, and generally gives an iterator that gives the values in the collection by-value. .iter is a naming convention for a method that takes the collection by-reference and returns an iterator that iterates also by-reference. Also, slice ([T]) does not implement IntoIterator, while &[T] does (which is also common for types that can be .iter-ed to have references to them implement IntoIterator)
I like your videos Jon, but can you avoid doing Tsskk sound every minute or so. It's very irritating to me someone suffering from misophonia. Thank you very much for the content 👍
As a Rust beginner, I find your videos invaluable. Thank you Jon !!
i exactly think the same. its crazy this is free content
Update on your expertise?
this is the best rust content produced, ever. Most rust devs or wannabes have programmed a computer before so will quickly get tired of "from zero" tutorials
I just learnt iterator design pattern and now this, was super relatable.
I love that your inclination is to show the correct use first. So many programmers preface their guides with the wrong way first and it makes me rage
it puts me on edge when I hear "so we could do it like this..." in a programming tutorial. I then have no idea if it's a valid alternative or just plain wrong until I watch the rest of the video
I am so, so, so tired of writing javascript and python. The most complicated thing I do all day is refactor crapping javascript formatting and try to convince people to follow better eslint rules, learn yet another annoying new npm package that probably has way to many dependencies, or have to constantly keep CTRL-F 'ing through a crappy python function that is 100 lines long. I am ready for Rust. Let's go!!
Great video. Great explanation. Thank you so much 🙏
I use typescript in fully strict mode, when I have to work with javascript.
Thank you Jon for all these learning materials. This is definitely helpful. I'm going through the whole crust of rust series and then I'll go through your book as well. I already book the pre-release. 💪😁
I am learning so much from you. Working with rust code from your streams and going from (kind of getting it) to (okay I actually GET it) feels oddly satisfying, and I have never felt like this while learning other languages over the years.
Really enjoying these Crust of Rust videos. The more lengthy videos are interesting, but I often feel lost halfway through. The shorter ones feel more relevant when you're still learning the basics but have gotten through the book.
10:00 into_iter takes the ownership of the value, which is the meaning of "into", the ownership is transfered "into" the iterator. value.into_iter() makes sense this way :)
@@YuruCampSupermacy correct :)
Yup, that's all correct. Keep in mind as I show later on though that `iter` is an inherent method, while `into_iter` is a trait method from the `IntoIterator` trait. Which means that other types and methods can be generic over it. This is why you'll still want to implement `IntoIterator` for, say, `&Vec` (which would effectively do the same as `Vec::iter`)
> into_iter takes the ownership of the value, which is the meaning of "into", the ownership is transfered "into" the iterator
True, though I want to point out something that had me confused for a long time, in case it helps somebody else: `into_iter` taking ownership initially led me to think that it always consumes the underlying collection. That's not necessarily the case though, because that "value" can be a reference, in which case `into_iter` takes ownership of a reference (a.k.a. precious little). E.g. there's an impl of `IntoIterator` for `&Vec`: doc.rust-lang.org/src/alloc/vec.rs.html#1971-1978 (which basically just defers to `Vec::iter`, as Jon says).
Here's a playground with a bit of code that helped me clear up that confusion: play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4a6c2e0230d98237e7ad86d239db4655
(I'm only part way through watching the video -- which is great as always, thanks! -- so apologies if this is addressed somewhere in there :) Still, hopefully someone might find it useful to have this information in the comments as well.)
@ Thanks David, now I get it!
I agree, the author didn't know this. "It’s easier to type" was his guess.
Thank you for the video! I have used iterators in other languages such as java and I am fairly familiar with the concept. But I still found your explanation useful.
Perfect presentation, I look forward to following your series as more presentations are made!
I'm leaning rust but... but one things I've started to notice (especially in this video ) is how crucial testing is and how rust makes it easy to encourage you to write them...
Thank you so much for these series. Really happy to see these type of videos about advanced Rust topics.
76K views should equal 76K likes. The value in this man's content is incomprehensible, in my opinion(s).
true, I recently have started to explore his playlists. he is next level programmer. He is bringing so much value , low level understanding of iterator, macros and other stuffs which we can use to rewrite the industry level framework stacks from scratch in rust itself. although i am keeping the hype aside. but the core rust is immense valuable and future .
Thank you for these videos! Very helpfull!
This is the next rust video I've watched
It's so great about how to think and try to implement by self to learn. By the way, is there any vim/nvim config in the video? It is good to have such lint related info or error show up. Thanks
Hey, can you put together a stream for the topic Smart pointers? I personally read the book about 2-3 times and still don't get the Cell and RefCell pointers.
How does flatten even call thr next() method in the iterator? All I see in code is a new() and getting a Flatten instance
Holy crap there is a person behind you. I did not see that until the end.
It would be cool to upload the streams as full-fledged YT streams such that the chat is also recorded and we can see it after the fact, but I am not sure if it is possible to cut the intro bootstrap part...
The live stream version with the comments is already linked at the bottom of the description :)
omg! That is crazy man! Jeg elsker det
Thank you very much for this nice series. Could you read the questions from comments slowly so that we can hear them?
I don't know if it's a conscious choice or you don't know about it, but coc offers rename refactoring with (coc-rename)
I noticed you do a lot of manual renaming, so I thought I'd point it out. I've personally got it mapped to F3
Hi Jon thanks for your evangelism on Rust, I have a tip for your videos on TH-cam you should if possible enable English subtitles
They are already enabled, but TH-cam unfortunately isn't great about adding automatic captions for longer videos. Community captions are enabled though if someone feels a calling!
I think the reason that `O::item::item` could not compile is because O::item could be associated constant. This is same with C++, where you need to use `typename` to tell the compiler that `item` is a type not a value.
9:58 iter and into_iter are 2 different things, aren't they? Iter is "read-only access" and into_iter is "consuming access"
In what way do they do same thing?
I think the idea is that a.iter() is effectively just an alias for &a.into_iter(), and a.iter_mut() is also effectively just an alias for &mut a.into_iter(). into_iter() will give "read-only access", "mutating access", or "consuming access" depending on the context. You could also think of it as consuming the references when called with a reference. iter() and iter_mut() are redundant but convenient.
I just realized that even after trying to learn Rust for over a year, I still don't understand so much of the basics. 😭
Have you tried to write something in Rust? If not, I would recommend to do so! As many things in life, you can only get better by practicing. :)
Great videos, thanks.
With the extension trait, will deep() work without "flatten(flatten" ? Your unit test only goes to two levels.
Rust is cool! U are cool 😎 too Jon!
I'm still about halfway through the video (44:50), and it seems we finished talking about flatten, but can't it be improved?
I don't see why it would need an Option for the inner iterator.
We could have new like this:
```
let inner = iter.next();
Flatten { outer: iter, inner }
```
and next like this:
```
loop {
if let Some(elem) = self.inner.next()
return Some(elem);
self.inner = self.outer.next()?;
}
```
This seems more elegant than the implementation of next that's shown in the video.
I can see that this wouldn't work for DoubleEndedIterator. We would need to call next_back in new, and so our Flatten wouldn't accept iterators that don't implement DoubleEndedIterator.
Although it seems to me that Flatten and DoubleEndedFlatten should have been two types anyways, given that they don't need the same fields...
Maybe in practice it's better to have one type for both use cases, but I'm not experienced enough to judge when that might be the case.
I find it interesting that they choose to use & when denoting a parameter must be borrowed (ref), but for patterns they use the 'ref' keyword. That tripped me up when I was first learning rust. Any thoughts on why they chose to spell it out in patterns (maybe just to indicate it's a pattern) ?
The two are actually not the same. They're more like opposites. A &foo in a pattern is "match foo as what's behind the reference", whereas ref foo is "match foo as a reference".
@@jonhoo - ah gotcha, definitely big difference there. much thanks
Thank you! This is great
What is your desktop setup? It looks quite nice!
I actually did a full video on that over at th-cam.com/video/ycMiMDHopNc/w-d-xo.html :)
Isn't your approach to implementing DoubleEndedIterator wasting a lot of memory in the case where we don't actually use DoubleEndedIterator?
I touch on this towards the end of the video, and why it currently has to be there. You're right that it's wasting a bit of memory, but keep in mind that it's just storing an extra iterator, which is itself unlikely to be very large (a hash map iterator for example is mostly just a couple of pointers). In practice, it's also unlikely to matter much, since it is stack allocated, and iterators are usually short-lived. It is unlikely that you have, say, hundreds of thousands of `Flatten` iterators active at the same time.
That's a pretty nice vim skin, which one is it?
It's called gruvbox-dark-hard :)
39:00 couldn't you just call inner_iter.into_iter().next() instead of doing all of that work with trait bounds?
On a side note, I think you hold the Guinness world record for saying iterator a gazillion times in an hour.
I wonder if this being a video about... iterators had any part in that.
@@meuko it was a joke man....jeez
@@user-ov5nd1fb7s Hey dude I was also joking, sorry
I feel like every topic you introduced you just said "but this isnt what we're here to talk about" or "but that isn't important right now" and it left me wondering what IS being demonstrated
I'm not sure if this gets answered later on or if I missed something, but regarding the Flatten implementation: is there a particular reason why O is bounded to Iterator, but O::Item is bounded to into Iterator? why not the other way round or have them both be the same?
I don't know if this became clearer throughout the stream, but the answer is that the outer iterator has to be Iterator because we store the outer iterator itself directly in the struct (if it was IntoIterator, and we stored a O::IntoIter, I _think_ the compiler would complain). We also don't _need_ the Into part of IntoIterator in Flatten, so why make the bound larger than it needs to be? For the inner iterator however, we _are_ going to turn each element of the outer iterator _into_ an Iterator, so we need it to implement IntoIterator. If it just implemented Iterator, then we'd need to mutate the iterator we're walking over directly!
At 42:00 you didn't remove `self.inner = None` from line 43. The tests pass without the line just fine. What is the reason for leaving it in?
It works fine because the inner iterator also keeps track of the fact that it's been exhausted. So next time around, we'll enter the outer `if let`, but the call to `next()` will yield `None`, and so we'll fall down to the code below again. But that means we're now relying on it being okay to call `next()` on the inner iterator after it has returned None, which is not the case for all iterators!
what about implementing the iter() trait for a LinkedList , its so hard !!!
Looks like someone is playing video game in the couch.
Why does &[T] slices implement iterator but normal arrays don't ?
Edit: from rust 1.54 the array primitive implement the iterator trait
Take a look at blog.rust-lang.org/2021/05/11/edition-2021.html#intoiterator-for-arrays, which gives a lot of context for that particular nuance.
This video has no subtitles, could anyone explain why?
Not sure I understand the purpose of associated types and why not use generics. If all they're for is as you said, to make life easier for the compiler and maybe shave off a second of compile time, why not just use generics and be done with it? surely there's more to it then that? I can't imagine the core team introduced this language "construct" just for that? Does it provide additional benefits?
As I alluded to in the video, they enable you to constrain implementation of the trait to only a single instance per type, which can often times be necessary to get type inference to work. Associated types also tend to be more ergonomic to use, precisely because they're more constrained. Take the Future trait for example - you don't need to specify the Output type every time you take a F: Future, because the Output type is associated, and thus dictated by F.
Jo(h)n gang!!!
Can anyone say if an Associated Type basically just a typedef nested into a type, or is there more to it than that? Sounded like a new concept or something when first learning rust, and some compare to generics, but it's hard-coded to some type (which may or may not be a generic type). But a direct comparison to generics doesn't really seem to make sense to me at all - even if the type being defined leverages generics. Associated type is just a nested typedef, or I am actually way off here?
Hi Jeff! An associated type is sort of like a nested typedef, but it is nested not per type, but per implementation of a trait. For example, if some type Foo implements the trait Bar twice for two different Ts, it can use different types for an associated type of Bar for those two impls. Associated types can also have trait bounds, and can be referenced elsewhere in the trait. For example, the Deref trait from the standard library has an associated type Target, and a method fn deref(&self) -> &Self::Target.
@@jonhoo - great answer, thanks
What is that drawing program?
It's called MyPaint :)
And now something completely different ;)
What is the name of this zsh option/extension which shows suggestion based on history? e.g. 2:38
I use the fish shell, not zsh, which does this by default :)
peeps, what is this font used here in vim? anyone know?
I think Monolisa.
@@well.8395 noto sans mono, actually. said in the q&a for july 23 today.
I have the feeling that your local IP address 10.0.1.27 is a play on 127.0.0.1.
Over some explaining concepts. Too little on syntax craziness.
I don’t think your explanation of .iter vs .into_iter is correct. .into_iter is a trait method that consumes the value, and generally gives an iterator that gives the values in the collection by-value. .iter is a naming convention for a method that takes the collection by-reference and returns an iterator that iterates also by-reference. Also, slice ([T]) does not implement IntoIterator, while &[T] does (which is also common for types that can be .iter-ed to have references to them implement IntoIterator)
I fell asleep while watching this video😭
I like your videos Jon, but can you avoid doing Tsskk sound every minute or so. It's very irritating to me someone suffering from misophonia. Thank you very much for the content 👍