Thanks for the video! Great explanations and examples as always :D Honestly, I don't only use enum dispatch as a last resort for better performance but rather because I think it's often more straight forward to work with. The only thing to keep in mind when it comes to efficiency is that a fat enum is always at least as big as the largest inner type. So if there are few way larger variants that occur less often, it might make sense to box them. I think that's a neat way to add indirection very selectively.
Just this week I was annoyed at the need for boxing and dynamic dispatch in many cases, and was thinking about how to solve it. I was actually considering essentially creating enum_dispatch. Well, and some shit that's probably not possible or would break all kinds of security guarantees, a kind of dynamic dispatch on stack objects, where instead of the enum discriminator you'd have the vtable pointer, and after that it would behave like a enum, with enough size for the largest variant. But since I likely don't have the knowledge to pull that off, even if it's possible without extending rustc, since this likely would not be faster than enum variants, and since this would cause issues when converting these values back to known types, I eventually came to the conclusion that simple enums with some kind of automatic trait implementation, probably macro based, for a common trait would be the best solution. Thats where I was, in all likelyhood it wouldn't have gone further, but now I learn that OF COURSE someone already implemented it. Even if it is a bit of a downer, on the bright side, it means I choose a decent implementation approach, and I can use this crate without having to figure it out myself (which would've took a long time, or never happened). So thanks for enlightening me!
Very concise and clear explanation of Rust's traits. I haven't heard the term "enum dispatch" before. It kind of reminds me of the type tagging concept that is discussed in Structure and Interpretation of Computer Programs. Thank you for your work and effort!
I vainly tried the other day to write a linked list in Rust without knowing much about it. I felt exactly like I did in this video, except without whatsoever an reassurance that I'd understand what to do when all the errors start rolling off the screen.
I've wondered about this exact class of problem in Rust for a while now. Thanks for walking us through these different paths (dynamic dispatch vs enum dispatch) in such a clear and concise way! I wish I could double thumbs-up this video
It can! You create a structure that has all the functions you would want for another structure to use. You then create a structure that holds a variable of the structure you originally created then create a trait that has a get and get_mut function that gets the variable data contained inside in structure that decides to use the original data type along with all the functions of the syructure you are getting. You implement the get and get_mut of the structure that is housing the data type structure and then you can call all the functions implemented through the trait which was originally implied by the structure.
Thanks! The rust-analyzer extension is the big one. I also love "Error Lens" and "Git Lens". I use vim keybindings too. I think these are the important ones!
Force of habit I guess! Sometimes traits are implemented for `&mut Vec` and `&mut [T]` and they do different things, so I'd rather be explicit. Unless clippy tells me I shouldn't and then I just fold.
Super interesting video. What vscode extension are you using to get the inline rust language server error / warning messages? I use rust-analyzer and find it quite annoying to have to hover warnings/errors to see the description in a popup.
Why does changing a &mut dyn Trait to T: Trait work? The parameter still references a trait so how can Rust know the size? Also, my understanding is that a reference parameter (e.g. &var) means only a pointer is passed, which has a fixed size so how does Rust not know the size?
It works because `T: Trait` comes with an implicit bound `T: Sized` - `Sized` being the bound that requires Rust to statically know a type's size. Rust only complains when you relax that bound (`T: ?Sized`) or use a concretely unsized type (`dyn Trait`). And you are correct that Rust knows the size of all references, but Rust was complaining about not knowing the size of the type behind the reference, not the reference itself.
I believe with generics without the dyn keyword, Rust will actually create a new version of the function for each type that is used, so then it is very clear that Rust knows the size, as the generated function only takes 1 type. And this is known as static dispatch, as opposed to dynamic. (as an aside, you can make this change by just replacing "dyn" with "impl", which is a nice bit of syntactic sugar)
Hi. I love your videos :D I was wondering how did you make it so that rust-analyzer shows these errors (as in 2:08) on the right and highlights the lines in red in VSC?
I admit I got lost in this one. I once tried to do triple dynamic dispatch in Rust for fun, but it didn't go very well. It feels sad that things you take for granted in other OOP languages are unnecessarily complicated in Rust. For example, any trait could be automatically implemented for a Box dyn of that trait. Or at least easily implemented with a macro. Also, not being able to call "end" with Box again feels very dumb. I'm not sure if there is an actual reason why it cannot be called, or if it is just the compiler being lazy and not putting in the instructions to make it work as expected.
@@Evan490BC I agree! Concepts eased a lot of the pain of C++ metaprogramming. But it also missed some very important opportunities that already were poorly addressed. Take for example the Rust's ability to automatically generate boilerplate code to interact with JavaScript and WebAssembly. That kind of metaprogramming IO and code generation is still very difficult in C++. Or take this compile-time smart diffing that speeds up Rust's Dioxus rendering, the kind of metaprogramming info needed to allow (and execute!) these optimizations just isn't there yet in C++ and that I think will be added to C++ when it becomes even more popular in rust: th-cam.com/video/4KtotxNAwME/w-d-xo.htmlm15s
90% of time was spent on code solving problems that have zero relevance to what you actually try to do. In fact I find this video so infuriating, I'm switching to Zig.
Yes that's.. kind of the whole purpose of the video. I created the code examples specifically to show off a series of problems folks often encounter. I think there's a lot of context missing here, about why these problems exist: there's very good reasons why they do, but we must look at disassembly, that would be a good follow-up 😊
Maybe you should rather switch to JS then. There you can change fundamental methods, classes, function signatures, etc. without adjusting the rest of your code and interpreters will still just try to run it without complaining. Awesome technology! /s
The short answer is performance! I like the idea of making a sequel from the perspective of someone who's low-key pissed at the previous video for running into so many issues and wondering why the heck any of that nonsense is needed.
I'm not sure the snark is warranted - the frustration is real, I tried addressing in that video, but I took for granted that viewers would know why it's worth it to go through with all of this, and that may have been a mistake.
Thanks for the video! Great explanations and examples as always :D
Honestly, I don't only use enum dispatch as a last resort for better performance but rather because I think it's often more straight forward to work with. The only thing to keep in mind when it comes to efficiency is that a fat enum is always at least as big as the largest inner type. So if there are few way larger variants that occur less often, it might make sense to box them. I think that's a neat way to add indirection very selectively.
Just this week I was annoyed at the need for boxing and dynamic dispatch in many cases, and was thinking about how to solve it.
I was actually considering essentially creating enum_dispatch.
Well, and some shit that's probably not possible or would break all kinds of security guarantees, a kind of dynamic dispatch on stack objects, where instead of the enum discriminator you'd have the vtable pointer, and after that it would behave like a enum, with enough size for the largest variant.
But since I likely don't have the knowledge to pull that off, even if it's possible without extending rustc, since this likely would not be faster than enum variants, and since this would cause issues when converting these values back to known types, I eventually came to the conclusion that simple enums with some kind of automatic trait implementation, probably macro based, for a common trait would be the best solution.
Thats where I was, in all likelyhood it wouldn't have gone further, but now I learn that OF COURSE someone already implemented it. Even if it is a bit of a downer, on the bright side, it means I choose a decent implementation approach, and I can use this crate without having to figure it out myself (which would've took a long time, or never happened).
So thanks for enlightening me!
Very concise and clear explanation of Rust's traits. I haven't heard the term "enum dispatch" before. It kind of reminds me of the type tagging concept that is discussed in Structure and Interpretation of Computer Programs. Thank you for your work and effort!
I vainly tried the other day to write a linked list in Rust without knowing much about it. I felt exactly like I did in this video, except without whatsoever an reassurance that I'd understand what to do when all the errors start rolling off the screen.
I've wondered about this exact class of problem in Rust for a while now. Thanks for walking us through these different paths (dynamic dispatch vs enum dispatch) in such a clear and concise way! I wish I could double thumbs-up this video
What an excelent video on the topic of traits and a bit of enums, especially enum dispatching!
Thank you for your blessing. I'll avoid premopt for a week thanks to you!
When he said "Box"... I felt that
It can! You create a structure that has all the functions you would want for another structure to use. You then create a structure that holds a variable of the structure you originally created then create a trait that has a get and get_mut function that gets the variable data contained inside in structure that decides to use the original data type along with all the functions of the syructure you are getting. You implement the get and get_mut of the structure that is housing the data type structure and then you can call all the functions implemented through the trait which was originally implied by the structure.
Hey amos, I really enjoy your articles! What is your VS code setup? Which extensions do you use for rust?
Thanks! The rust-analyzer extension is the big one. I also love "Error Lens" and "Git Lens". I use vim keybindings too. I think these are the important ones!
I thought browsers nowadays lazily load images by default. When I scroll down I always see a blank square for a second before the image pops in.
What compels you to write things like `&mut output[..]` and `&output[..]` in general vs relying on Deref?
Force of habit I guess! Sometimes traits are implemented for `&mut Vec` and `&mut [T]` and they do different things, so I'd rather be explicit. Unless clippy tells me I shouldn't and then I just fold.
When will "Sorry Esteban
Super interesting video. What vscode extension are you using to get the inline rust language server error / warning messages? I use rust-analyzer and find it quite annoying to have to hover warnings/errors to see the description in a popup.
That's the "Error Lens" VSCode extension.
@@fasterthanlime thank you!
Why does changing a &mut dyn Trait to T: Trait work? The parameter still references a trait so how can Rust know the size? Also, my understanding is that a reference parameter (e.g. &var) means only a pointer is passed, which has a fixed size so how does Rust not know the size?
It works because `T: Trait` comes with an implicit bound `T: Sized` - `Sized` being the bound that requires Rust to statically know a type's size. Rust only complains when you relax that bound (`T: ?Sized`) or use a concretely unsized type (`dyn Trait`). And you are correct that Rust knows the size of all references, but Rust was complaining about not knowing the size of the type behind the reference, not the reference itself.
I believe with generics without the dyn keyword, Rust will actually create a new version of the function for each type that is used, so then it is very clear that Rust knows the size, as the generated function only takes 1 type. And this is known as static dispatch, as opposed to dynamic.
(as an aside, you can make this change by just replacing "dyn" with "impl", which is a nice bit of syntactic sugar)
Hi. I love your videos :D
I was wondering how did you make it so that rust-analyzer shows these errors (as in 2:08) on the right and highlights the lines in red in VSC?
Error lens extension :)
I admit I got lost in this one. I once tried to do triple dynamic dispatch in Rust for fun, but it didn't go very well.
It feels sad that things you take for granted in other OOP languages are unnecessarily complicated in Rust.
For example, any trait could be automatically implemented for a Box dyn of that trait. Or at least easily implemented with a macro.
Also, not being able to call "end" with Box again feels very dumb. I'm not sure if there is an actual reason why it cannot be called, or if it is just the compiler being lazy and not putting in the instructions to make it work as expected.
Loved it ❤️
please make more videos :)
Do you really type that fast or are those bits sped up?
The parts where I don't talk are at 2x speed. But I am a pretty fast typist to start with. My livelihood literally depends on it!
that's dope thanks
Typicsl coding session in Rust. Lovely
I'm not a rust dev and this video gave me anxiety. All of that for implementing a simple third library html parser???
accidental nonbinary pride flag at 0:54?
I mean... Rust is a great language, but this is just playing whac-a-mole.
I still would take this than debugging a template-heavy C++11 library. At least here the errors point to the actual errors on the source code.
@@empresagabriel true
@@empresagabriel Absolutely! Although, to be fair, things are better now with Concepts.
@@Evan490BC I agree! Concepts eased a lot of the pain of C++ metaprogramming. But it also missed some very important opportunities that already were poorly addressed. Take for example the Rust's ability to automatically generate boilerplate code to interact with JavaScript and WebAssembly. That kind of metaprogramming IO and code generation is still very difficult in C++.
Or take this compile-time smart diffing that speeds up Rust's Dioxus rendering, the kind of metaprogramming info needed to allow (and execute!) these optimizations just isn't there yet in C++ and that I think will be added to C++ when it becomes even more popular in rust: th-cam.com/video/4KtotxNAwME/w-d-xo.htmlm15s
90% of time was spent on code solving problems that have zero relevance to what you actually try to do. In fact I find this video so infuriating, I'm switching to Zig.
Yes that's.. kind of the whole purpose of the video. I created the code examples specifically to show off a series of problems folks often encounter. I think there's a lot of context missing here, about why these problems exist: there's very good reasons why they do, but we must look at disassembly, that would be a good follow-up 😊
Maybe you should rather switch to JS then. There you can change fundamental methods, classes, function signatures, etc. without adjusting the rest of your code and interpreters will still just try to run it without complaining. Awesome technology!
/s
@@fasterthanlime yes that would be very welcome. What does the added complexity give us in return, why is it justified?
The short answer is performance! I like the idea of making a sequel from the perspective of someone who's low-key pissed at the previous video for running into so many issues and wondering why the heck any of that nonsense is needed.
I'm not sure the snark is warranted - the frustration is real, I tried addressing in that video, but I took for granted that viewers would know why it's worth it to go through with all of this, and that may have been a mistake.
what is the purpose of you speeding up through your code modifications? @fasterthanlime