For newbies and people from other languages it might be helpful to say that traits are similar to interfaces in other languages like C# and Java - without the field stuff as you noted. Traits inverse the control - in other languages you define the interfaces first, then let types (classes) inherit them - in Rust you can arbitrarily define and use traits for existing objects (structs) even if you don't have access to the source code.
I read the first half of the Rust book. then I found this channel. Watching these videos is way easier to learn Rust with than reading that big book lol. I can just sit back and learn.
Pretty much same as Golang. Passing interfaces in function argument does the same thing in Golang too. Its a runtime check by go runtime to figure out which object it is vs the generic static dispatch. So, Generic faster than Traits objects, but trait objects have more flexibiltiy in writing clean code
Great video. After watching the video I feel more comfortable with trait objects. But I am not able to find RFC for "object safe" as mentioned by you in the video. Am I missing something.
This is the highest quality telling of the rust book I’ve seen online. It is a very longform piece of information though, so that’s not very friendly to the algorithm. Incredibly solid content in this playlist tho.
Love the video. I have one question : So in the case of dynamic dispatch, the rust compiler does some computation at runtime and hence we have a runtime cost. But how is a compiler working at runtime? I used to think the compiler after compiling the code is done with its duty and after that runtime libraries do all the remaining stuff?
I don’t know if you figured this out until now, but here is how I understand this (props to Jon Gjengset, you should watch some of his videos): Basically when using dynamic dispatch the trait object basically stores two pointers. One for the data that concrete type stores (in this video Button or SelectBox for example) and one for the vtable (virtual table) which stores the functions implemented for that type with the given trait. The compiler basically generates code to follow the pointer through vtable. So when you have a trait object which implements draw, it basically has to figure out the correct method. This is done by figuring out which type is it, then following the pointer to the vtable to find the correct method. This is the runtime cost mentioned in the video. So basically, yes you are right, compiler does nothing at runtime. It generates code which affects runtime. I hope you find this helpful (P.S. There may be inaccuracies in my explanation)
@@n.fejzic When I implemented interfaces in C the interface struct would have a bunch of function pointers and a pointer to the struct which has implemented it (pointer to data, basically), but I see that my approach was redundant. My approach meant that every interface struct had 5 function pointers if that interface had 5 methods. So, if I had 10 interface objects I would need memory for 50 function pointers. With the solution that you have explained you would replace these 50 function pointers with 10 pointers to Vtables and that VTable would have 5 function pointers, so 15 pointers in total. Every struct that implements an interface (or trait as it is called in Rust) will produce a global constant (immutable) instance of the VTable of the interfaces/traits that it implements. Next time I simulate OOP in C I will do it that way. I used global, const (immutable) VTables in order to implement polymorphism with class inheritance, but yeah, they are the more efficient solution for emulating interfaces/traits as well.
Nice explanation. Still wondering why Box() is needed around the different objects though, I know it has something to do with them being of different types, but still not sure why this further level of abstraction is needed.
Since they are different types, they can have different sizes but a size must be known during compilation. Since Box is a pointer, it has the same size no matter what it wraps
Yes, a Vec is effectively a pointer and has the same size regardless of what it wraps so you don't need to encase it into a Box for the compiler to know its size
The explanation is great! However it is crippled by the visualization since the screen can only display so much code. Like when you add new methods I have to go back on the video to see the struct again to understand what it does. This and the Smart Pointers chapter made it hard to learn from the video alone since the code is so long to backtrack everytime. An easy way would be to compliment the video with the rust book. But then a text to speech reader would make watching the video unecessary. It might be too late now for suggestions but creating your own shorter examples would be much better rather than following the examples on the book.
The various members of an enum are the same type. The compiler can't tell them apart. By using distinct types, the compiler can dispatch different code for each of the alternatives.
Rust and Golang are my favorite language as of now, unfortunately there isn't any good GUI library available to my knowledge. Have u use GUI in either language?
You can write your backend in Rust and then call the code from a language that has good GUI libraries, like Kotlin's TornadoFX or some C# GUI framework
iced and egui as pure rust solutions are probably the most popular ones. And there is gtk 3/4 bindings for rust as well. But ofc the language is not really mature in this area.
Or...are these separate entities and just collocated?(i.e it's equivalent to defining a separate lib and specifying it in the cargo.toml file of the bin crate)
Every crate is 1 library with 0 or more binaries where the binaries can use code from the library (which can be empty) in the same manner as users of your crate would
There are a few shortcomings in this video. 1. What you say about draw() being in the base class in OOP is incorrect. That's more like an interface. Behavior is modeled by interface (as trait in Rust). That is, the right way to do is to declare an interface then the base class optionally provides the default impl. 2. What class inheritance is for is for code reuse. In your GUI example, suppose 90% of draw() is the same for each Draw component. Suppose that 90% of code needs the state (e.g., coordinates that are present in all components) of the component. Do we have to copy and paste the same code for draw() in each component? Now, suppose you have like 10 of those methods. What's Rust way to deal with code reuse? Thus, your omission of the impl misses the most important benefit of class inheritance.
I would say that Rust, preferes composition over inheritance. So the way that Rust enables polymorphism is in the form of composition. Because it's a fact that inheritance is the devil...
No shit Sherlock! That's literally what he says at the beginning of the series "for those who prefer video style tutorials, we'll go through 'The Book' together". Sure helps me a lot, I keep falling asleep when trying to read the Rust Book.
📝 Get your *FREE Rust cheat sheet* : www.letsgetrusty.com/cheatsheet
Man, it's amazing how easily you make it seem like concepts I couldn't understand. Thanks for your work!!
Did you read the rust book? It’s very well explained there
For newbies and people from other languages it might be helpful to say that traits are similar to interfaces in other languages like C# and Java - without the field stuff as you noted.
Traits inverse the control - in other languages you define the interfaces first, then let types (classes) inherit them - in Rust you can arbitrarily define and use traits for existing objects (structs) even if you don't have access to the source code.
I read the first half of the Rust book. then I found this channel. Watching these videos is way easier to learn Rust with than reading that big book lol. I can just sit back and learn.
This is such a timely video for me. Really appreciate you explaining why generics aren’t always appropriate solution 👍
I hope more people will subscribe. You're creating the best teaching material for Rust! Thanks a lot!
I'm so glad the first language I learnt was C++ (although, that was 6 years ago). A lot of these concepts I kind of know, just rusty :D
Yep coming from C++ everything else is easy :)
Even rust has some higher level language abstractions as well, something like channels which is used n Kotlin & Go !!
Should make a series going through the traits in the standard library. Would be helpful
So glad I found this. I've been breathing my head against a wall trying to use egui and it's spotty docs
Needed this, thanks! I rave about you and I pray that others watch you content too.
Oh shoot. We got something in common
Great video thank you, that's just what I needed to understand
у вас дуже гарний і корисний канал, дякую.
Pretty much same as Golang. Passing interfaces in function argument does the same thing in Golang too. Its a runtime check by go runtime to figure out which object it is vs the generic static dispatch. So, Generic faster than Traits objects, but trait objects have more flexibiltiy in writing clean code
excellent as always , waiting for the cheatsheet
Great video! Thanks for this content!
Great video. After watching the video I feel more comfortable with trait objects. But I am not able to find RFC for "object safe" as mentioned by you in the video. Am I missing something.
Man, i just want to see your channel succeed :(
This is the highest quality telling of the rust book I’ve seen online. It is a very longform piece of information though, so that’s not very friendly to the algorithm.
Incredibly solid content in this playlist tho.
Love the video. I have one question :
So in the case of dynamic dispatch, the rust compiler does some computation at runtime and hence we have a runtime cost. But how is a compiler working at runtime? I used to think the compiler after compiling the code is done with its duty and after that runtime libraries do all the remaining stuff?
I don’t know if you figured this out until now, but here is how I understand this (props to Jon Gjengset, you should watch some of his videos):
Basically when using dynamic dispatch the trait object basically stores two pointers. One for the data that concrete type stores (in this video Button or SelectBox for example) and one for the vtable (virtual table) which stores the functions implemented for that type with the given trait.
The compiler basically generates code to follow the pointer through vtable.
So when you have a trait object which implements draw, it basically has to figure out the correct method. This is done by figuring out which type is it, then following the pointer to the vtable to find the correct method. This is the runtime cost mentioned in the video. So basically, yes you are right, compiler does nothing at runtime. It generates code which affects runtime. I hope you find this helpful (P.S. There may be inaccuracies in my explanation)
@@n.fejzic Very helpful :)
I believe that dyn X is a tuple of an X and a table of function pointers for each operation in that trait X. That is all defined at compilation time.
@@n.fejzic When I implemented interfaces in C the interface struct would have a bunch of function pointers and a pointer to the struct which has implemented it (pointer to data, basically), but I see that my approach was redundant. My approach meant that every interface struct had 5 function pointers if that interface had 5 methods. So, if I had 10 interface objects I would need memory for 50 function pointers. With the solution that you have explained you would replace these 50 function pointers with 10 pointers to Vtables and that VTable would have 5 function pointers, so 15 pointers in total. Every struct that implements an interface (or trait as it is called in Rust) will produce a global constant (immutable) instance of the VTable of the interfaces/traits that it implements.
Next time I simulate OOP in C I will do it that way. I used global, const (immutable) VTables in order to implement polymorphism with class inheritance, but yeah, they are the more efficient solution for emulating interfaces/traits as well.
Nice explanation. Still wondering why Box() is needed around the different objects though, I know it has something to do with them being of different types, but still not sure why this further level of abstraction is needed.
Since they are different types, they can have different sizes but a size must be known during compilation. Since Box is a pointer, it has the same size no matter what it wraps
Can you use `Vec` instead of `Vec`?
Yes, a Vec is effectively a pointer and has the same size regardless of what it wraps so you don't need to encase it into a Box for the compiler to know its size
As always. Great video.
The explanation is great! However it is crippled by the visualization since the screen can only display so much code. Like when you add new methods I have to go back on the video to see the struct again to understand what it does. This and the Smart Pointers chapter made it hard to learn from the video alone since the code is so long to backtrack everytime.
An easy way would be to compliment the video with the rust book. But then a text to speech reader would make watching the video unecessary.
It might be too late now for suggestions but creating your own shorter examples would be much better rather than following the examples on the book.
Nice, I remember a comment I made on one of your videos about this!
Should I use OrbTk or Iced
At this point I would suggest people at RUST to just add a link to his videos in rust lang book
what's the addon you're using for auto complete suggestions?
Tyvm Bogdan
Very Nice and Awesome
couldn't you just use enums, if you only have a limited amount of possible types ?
The various members of an enum are the same type. The compiler can't tell them apart. By using distinct types, the compiler can dispatch different code for each of the alternatives.
So many new languages have been developed lately. Has anyone tried Carbon the C++ successor? I wish I could master them all! Lol
That would be fun, ngl I wish I had this drive to learn all these languages during covid lockdowns, better late than never I guess ha ha
too late but cant' we use Enum to do this ?
A user of your third party library can't create their own type that still works with your library's functionality if you use an enum
Rust and Golang are my favorite language as of now, unfortunately there isn't any good GUI library available to my knowledge. Have u use GUI in either language?
There isn't any good GUI library in any language
You can write your backend in Rust and then call the code from a language that has good GUI libraries, like Kotlin's TornadoFX or some C# GUI framework
iced and egui as pure rust solutions are probably the most popular ones. And there is gtk 3/4 bindings for rust as well. But ofc the language is not really mature in this area.
@@----__--- egui is very easy to use and has a nice api.
xaml via windows-rs
Man, thank you.
Interesting, in this video a crate had both an executable main.rs and a lib.rs. does this mean that we can have a library and bin crate combined?
Or...are these separate entities and just collocated?(i.e it's equivalent to defining a separate lib and specifying it in the cargo.toml file of the bin crate)
he explains it in the early video
yes, you can have library and binary crates combined
Every crate is 1 library with 0 or more binaries where the binaries can use code from the library (which can be empty) in the same manner as users of your crate would
For Gophers, traits are just interfaces. Period. Easy
There are a few shortcomings in this video. 1. What you say about draw() being in the base class in OOP is incorrect. That's more like an interface. Behavior is modeled by interface (as trait in Rust). That is, the right way to do is to declare an interface then the base class optionally provides the default impl. 2. What class inheritance is for is for code reuse. In your GUI example, suppose 90% of draw() is the same for each Draw component. Suppose that 90% of code needs the state (e.g., coordinates that are present in all components) of the component. Do we have to copy and paste the same code for draw() in each component? Now, suppose you have like 10 of those methods. What's Rust way to deal with code reuse? Thus, your omission of the impl misses the most important benefit of class inheritance.
Yo any ideas what kind of a project I should work on? I finished the book
You can contribute to existing open source Rust projects.
"raytracing in a weekend" but do it in rust rather than c++.
I would say that Rust, preferes composition over inheritance.
So the way that Rust enables polymorphism is in the form of composition.
Because it's a fact that inheritance is the devil...
gooey?
OOP is sinking ship. :)
nope.
you can have a vector of enums
With all due respect, all your videos are just carbon copies from the Rust Programming Language book.
so what
No shit Sherlock! That's literally what he says at the beginning of the series "for those who prefer video style tutorials, we'll go through 'The Book' together".
Sure helps me a lot, I keep falling asleep when trying to read the Rust Book.