Rust Powered Polymorphism ⚡️ With Traits

Поделиться
HTML-код
  • Опубликовано: 21 ноя 2024

Комментарии •

  • @narigoncs
    @narigoncs Год назад +40

    Worth mentioning that `impl Trait` is just syntax sugar for `fn road_trip(vehicle: &T) {}`

  • @mk72v2oq
    @mk72v2oq Год назад +361

    Worth mentioning that "prefer composition over inheritance" principle is general for any language. Rust just takes it as the standard and makes inheritance obsolete.

    • @codetothemoon
      @codetothemoon  Год назад +67

      very true, good point! ditching inheritance makes code infinitely more readable imo...

    • @phenanrithe
      @phenanrithe Год назад +36

      That's a common misunderstanding of OO techniques. Composition and inheritances are two different patterns for two different needs (search for "has-a" and "is-a"), one doesn't simply replace the latter with the former. Rust just lacks inheritance at the moment, which leads to a class of issues for specific problems to solve, unfortunately. It's possible to work around but it requires to duplicate the code, which makes it hard to maintain. Or by using macros, which makes reading the code much more difficult.

    • @techpriest4787
      @techpriest4787 Год назад +5

      Yes. Mostly. For game engine frameworks for instance you will need to store all data in one place like a vec for centralized storage so that you can do multi threading. That requires down casting which again requires up casting to Any first when you need access to data. For now up casting is an unstable feature. But it seems to work so far. The problem is that if you did cast to Any via your own functions inside a custom trait that is not Any. That you have to do that for any game node all over again because you can not make a default trait because of life time issues and seized issues. Horrible pain in the neck. But up casting is being stabilized so it is coming to stable Rust in the future.

    • @JorgetePanete
      @JorgetePanete Год назад +11

      @@phenanrithe Can you give examples? It would help

    • @ozanmuyes
      @ozanmuyes Год назад +5

      Is Rust even an object-oriented language?

  • @Possseidon
    @Possseidon Год назад +332

    This would've probably went over the scope of what you were trying to show but for those interested:
    If you want anything that implements both `LandCapable` and `WaterCapable` to automatically implement `Amphibious` as well, you can do so:
    impl Amphibious for T {}
    Which can be read as "implement `Amphibious` for any type T that implements `LandCapable` and `WaterCapable`".
    There is also alias traits in unstable, that basically do more or less the same thing but with less boilerplate:
    trait Amphibious = LandCapable + WaterCapable;

    • @codetothemoon
      @codetothemoon  Год назад +57

      Great point, I probably should have covered this as well - it would make things much more concise if you have lots of amphibious vehicles

    • @Singhrager
      @Singhrager Год назад +13

      @@mathijsfrank9268 I think you may be reading it the wrong way around. The example does not mean that being Amphibious implies being LandCapable. It means that, if you are LandCapable (regardless of whether you customize drive) and you are WaterCapable, that implies that you are Amphibious. So you still only have one implementation of drive, i.e. the one you go with when you implement LandCapable.

    • @tsalVlog
      @tsalVlog Год назад +8

      oh man, every day, after 5 years, I still learn something new about Rust.

    • @JorgetePanete
      @JorgetePanete Год назад

      gone*

    • @ythanzhang
      @ythanzhang Год назад +7

      You can also make T ?Size so it works on trait objects too.
      impl C for T {}

  • @Julian-vi4vl
    @Julian-vi4vl Год назад +53

    One place where you really cannot escape the dynamic dispatch is when you have mixed types like a vector that can contain both SUV and Sudan

    • @shooohm405
      @shooohm405 2 месяца назад +2

      Put both SUV and Sedan in an enum and use the enum in the vector.

    • @stefanmadzharov9170
      @stefanmadzharov9170 День назад

      @@shooohm405 This is however trading memory for runtime performance if the structs have different memory footprints.

  • @narwodev
    @narwodev Год назад +45

    Your clear and concise way of explaining things really helps me to learn more about Rust. Looking forward to the next episode...Thank you!

    • @codetothemoon
      @codetothemoon  Год назад +8

      WOW thank you so much Mark!! I really appreciate your support and your kind words. Thanks again for watching!

  • @cd-bitcrshr
    @cd-bitcrshr Год назад +11

    Was stuck wondering how to do something in a Rust project, and you just went ahead and put the solution on a platter for me. Excellent video, as always.

  • @fayaz2956
    @fayaz2956 Год назад +6

    Whoa!! the way you explained the zero cost abstraction is mind blowing!!

  • @BulbaWarrior
    @BulbaWarrior Год назад +34

    I think it's important to note a naming convention for traits used in rust: if your trait implements one function it should be named the same way as the function. So in this example idiomatic names for LandCapable and WaterCapable would be "Drive" and "Float". It is nice to know the convention as many of the standard traits are named this way (for example From and IntoIterator)

    • @sebastiangudino9377
      @sebastiangudino9377 Год назад +2

      Pretty sure the implications is that being "LandCapable" will do much more than just provide a single drive method.
      And on a small tangent. If your trait IS just what function. What's really the benefit os using a trait over just writing a generic drive function that you call as: drive(suv)?
      (Genuine question. Since I would be surprised if rust made some nice optimization in one case vs the other)

    • @BulbaWarrior
      @BulbaWarrior Год назад +4

      @@sebastiangudino9377 I think you are mixing things together. LandCapable is not a type, its a trait, so you can't pass it as a generic parameter. The correct call would be drive::(suv), but then think about what drive does: it has to work for all types that implement LandCapable, which means that you can only use functions from the LandCapable trait. But we have just decided to move the only function out of LandCapable, so drive can't really do anything meaningful now.
      Now let's look at why having a single function in a trait is a good thing. Suppose LandCapable has another method, I will call it fuel(). Now look at road_trip(): it only cares that its argument implements drive() and does not really care about fuel, so fuel() is redundant here, now imagine we have a bike that can't really be fueled. A better more flexible approach would be to have two traits: Drive and Fuel. Then functions that care only about drive will use just the first trait and some other functions will require both traits to be implemented - your code is more flexible!
      TLDR: think of traits not as objects but properties of objects, which work better when they are granular. Hopefully I made my point clear

    • @sebastiangudino9377
      @sebastiangudino9377 Год назад +1

      @@BulbaWarrior Ups yeah, i mixed the Syntax up quite a lot. But you thankfully understood what I meant! And yeah, I guess you are right. Rust is deeply related to composition. Weird at first. But seems like a really nice abstraction as things scale up

  • @ygypt
    @ygypt 2 месяца назад

    Every time I lookup something new that I'm learning, I have to wade through numerous videos with some guy talking forever about nothing and saying words I dont understand before finally stumbling across the quick and concise explanation that actually helps me. This time, I found it right away :)

  • @sofroneageorgian7494
    @sofroneageorgian7494 3 месяца назад +2

    At 7:28 I paused and said damn, this guy is really good at naming things.

  • @aditeya1024
    @aditeya1024 Год назад +16

    I was writing a ray tracer in rust and this video was super helpful in explaining a trait!

    • @codetothemoon
      @codetothemoon  Год назад +6

      nice, really happy that it helped! and props to you for tackling a ray tracer, that's quite an undertaking!

    • @Redditard
      @Redditard Год назад +3

      i hope you'll succeed and i'll be really happy if you'll let us know!!! all the besttt

    • @gaxkiller
      @gaxkiller Год назад

      What do you use to draw a single pixel on screen ? Was interested to learn rust doing a raytracer, even a basic one who does not use the gpu at first

    • @aditeya1024
      @aditeya1024 Год назад

      @@gaxkiller i use the ppm format, which just writes the rgb values of each pixel. Though I think you can use the image crate to convert to jpg/png

    • @gaxkiller
      @gaxkiller Год назад

      @@aditeya1024 Thx ! Ok so you write in a file, not on screen, perfectly fine but you cannot do "interactions" (move the camera, etc)

  • @Kodlak15
    @Kodlak15 Год назад +22

    This aspect of Rust has been really hard for me to wrap my head around. Perhaps even more so than ownership/borrowing/lifetimes. Thanks as always for the great content. This was helpful 😁

    • @codetothemoon
      @codetothemoon  Год назад +1

      Nice, really happy it helped!

    • @nahiyanalamgir7056
      @nahiyanalamgir7056 Год назад

      I think it's because most people didn't learn these concepts of composition as beginners to programming. We instead swallowed OOP principles like that of Java. But things are changing and Rust is designed to be powerful, not simple and easy-to-learn.

    • @urisinger3412
      @urisinger3412 Месяц назад

      ​@@nahiyanalamgir7056the first form of oop in java I learned as interfaces, which are really similar to traits

  • @ukaszlampart5316
    @ukaszlampart5316 Год назад +7

    There are two very practical advantages of traits compared to inheritance base polymorphism: automatic implementation (if your type implements other specific traits), and automatic derivation. Because traits are seperate piece of code from your datatype it is ok to generate implementation for them automatically as this does not affect existing code base, compared to generating methods for interfaces.

  • @sergiuoanes4635
    @sergiuoanes4635 Год назад +35

    Absolutely love your videos. Simple examples straight to the point. Keep them coming! thanks

    • @codetothemoon
      @codetothemoon  Год назад +3

      wow thank you so much for your support and kind words! more to come!

  • @marcusrehn6915
    @marcusrehn6915 Год назад +17

    This feels like a far superior way to do OOP than what I am used to see in other languages.

    • @codetothemoon
      @codetothemoon  Год назад +4

      I like it as well!

    • @olafk8232
      @olafk8232 Год назад

      What advantage do you mean?

    • @Tienisto
      @Tienisto 11 месяцев назад +3

      It might be safer but it is not superior compared to traditional OOP languages because you need to write much more code

    • @marcusrehn6915
      @marcusrehn6915 11 месяцев назад +1

      @@Tienisto Compared to Java or C++?

    • @collynchristopherbrenner3245
      @collynchristopherbrenner3245 2 месяца назад

      @@Tienisto The benefit though is that you get native readability, so readability is encouraged by the language instead of having to be implemented through discipline. This seems like a much better incentive structure for writing high quality (cleaner/more readable) code. Of course, Rust is still a complicated language, so there is a trade-off at some point.

  • @kippie80
    @kippie80 Год назад +13

    Didn't realize &impl and &dyn were so interchangeable, thanks!

    • @codetothemoon
      @codetothemoon  Год назад +2

      nice, glad you found it valuable!

    • @phenanrithe
      @phenanrithe Год назад +8

      They are not really interchangeable. Using "impl" is just syntactic sugar for a generic function, so as explained in the video the compiler will instantiate this function for each type that is used. This also means you cannot use it for example with a collection holding different types with the trait - for that you must use "dyn" and its virtual table (and that collection will also need to use fat pointers).

  • @rct999
    @rct999 11 месяцев назад +2

    Tnx! This actually helped me understand the trait system in rust. I know 'traits' from PHP, but I never found a situation where it could be useful (its just horizontal inheritance basically). The way Rust uses traits makes much more sense to me.

    • @codetothemoon
      @codetothemoon  10 месяцев назад

      nice! really happy the video helped you understand Rust traits!

  • @creativecraving
    @creativecraving 9 месяцев назад +1

    This video helped me understand the difference between the dyn and impl syntax. Thank you!

    • @codetothemoon
      @codetothemoon  9 месяцев назад +1

      nice, really happy you got something out of it!

  • @johndavidson8096
    @johndavidson8096 Год назад +1

    Amazing video. You break down these rust concepts better than anyone else! Much appreciated as a TS/JS dev!

  • @Panakotta000
    @Panakotta000 Год назад +6

    Dynamic dispatch is also extremely good when combined with Reference-Counting f.e. "Rc"
    and combined with Any we can even do some limited downcasting
    "prefer composition over inherintance" sure... but ngl... sometimes if you dont just have shared functionallity... but also shared data... it becomes a mess of delegations... which imo is not ideal...

  • @yondaime500
    @yondaime500 Год назад +2

    One thing that is hard to do in Rust compared to other languages like C++ and C# is casting a trait to another trait. Like if you have a Box, you cannot check at runtime if the type stored in that variable also implements WaterCapable, whereas is C++ you can use dynamic_pointer_cast for that, and in C# you have the "is" and "as" keywords. I mention it because I've been rewriting code from those two languages to Rust, and I'm struggling to come up with the right patterns to replace these dynamic casts.

    • @PeterAuto1
      @PeterAuto1 Год назад +1

      you could add a try_as_x() method to the trait, that in the default implementation returns None, but you can override it so it convers the type.
      like
      struct Foo;
      trait X {
      fn try_as_y(&self)->Option {None}
      }
      trait Y {
      fn try_as_x(&self)->Option {None}
      }
      impl X for Foo{fn try_as_y(&self)->Option {Some(self)}}}
      impl Y for Foo{
      fn try_as_x(&self)->Option {Some(self)}}

    • @yondaime500
      @yondaime500 Год назад +2

      @@PeterAuto1 It's just kind of awkward that I have to list out all the traits I want to be able to cast into, and then remember to override the cast function for every type that implements the corresponding trait. I had found some crates that apparently generate this boilerplate code with macros, but they don't have that many downloads, so I figured maybe I just shouldn't be doing that in the first place. But thanks for the tip anyway.

    • @heruhday
      @heruhday Год назад

      You could use trait downcast

    • @homelikebrick42
      @homelikebrick42 Год назад

      i know this is an old comment, but there is std::any::Any in the standard library for this exact thing

    • @yondaime500
      @yondaime500 Год назад +1

      @@homelikebrick42 Not quite. Any only allows casting to a concrete type. What I wanted was to cast a trait to another trait. For example, given a list of dyn Sensor, get all the items that implement the Thermometer trait, and then call a method from that trait. This is what is easy to do in C# and C++ but not in Rust. In Rust I have to add a method to Instrument with a default implementation (so that things that are not Thermometer don't have to implement it just to return an error), and then remember to override it where needed. It's kind of annoying. So now I'm trying to unlearn years of OOP so I can structure my code better to stop fighting against the language and avoid the problem altogether.

  • @anuradhaamarasinghe9649
    @anuradhaamarasinghe9649 Год назад +2

    Oh man, I'm so glad that I subscribed to this channel. I think your explanation is obvious. Thank you and keep up the good work.

  • @nickpatella1525
    @nickpatella1525 Год назад

    Darn, I didn't even realize you could choose between dynamic dispatch and static dispatch so concisely. And I didn't know about supertraits.

  • @grahamcracker1234
    @grahamcracker1234 Год назад +3

    Great video, just wanted to let you know that for struct naming semantics when you have multiple capital letters next to each other you are actually only supposed to capitalize the first one, e.g. Suv not SUV, Html not HTML

  • @danielgerlach5141
    @danielgerlach5141 Год назад +5

    Great video about rust traits, especially the super trait was very interesting.
    Of topic question: what editor do you use? I remember you used vscode, but now it seems you switched to (neo)vim? Btw it also seems you have a new keyboard - not so clicky and easier on the ears for the video 👍

    • @awdsqe123
      @awdsqe123 Год назад +1

      The editor is doom emacs.

    • @codetothemoon
      @codetothemoon  Год назад +4

      thanks, really glad you liked it! I'm currently using DOOM emacs which I'm really enjoying at the moment. And I am also trying a new keyboard called a Corne - it's pretty amazing and I can't see myself going back at this point

    • @codeshowbr
      @codeshowbr Год назад

      @@codetothemoon do you have your doomemacs config shared somewhere? I tried to enable inlay hints but failed.

    • @codetothemoon
      @codetothemoon  Год назад +2

      @@codeshowbr actually i don't believe I did anything special other than uncommenting (rust +lsp) in init.el, and I *think* also uncommenting "lsp" in the "tools" section

  • @wasabinator
    @wasabinator 2 месяца назад +1

    Just getting into Rust and this clarified a lot from me. Thanks.

    • @codetothemoon
      @codetothemoon  2 месяца назад

      Nice, really happy it was helpful for you!

  • @Bvngee
    @Bvngee Год назад +2

    I've noticed you seem to be editor-hopping a lot :P first VSCode, then nvim, then helix, now doom emacs?? What are your thoughts on what you'll settle with or is this just for fun?

  • @jm-alan
    @jm-alan Год назад +6

    I've been working with Rust, learning on my own, for like a year now, and just today from this video learned that dyn is specifically referring to dynamic dispatch 😅
    Like, duh, that makes perfect sense, but I just kinda took it as "dynamic means runtime processed aka vtable lookup" without stopping to think

    • @codetothemoon
      @codetothemoon  Год назад +2

      nice, really glad you got something out of the video, despite likely already being familiar with many of the concepts!

  • @Feyon
    @Feyon Год назад +1

    Very focused and clear explanation 👍 The impl instead of dyn was something new to me

  • @joshuarose3186
    @joshuarose3186 Год назад +1

    Hi there, i was wondering if you had a link to your dotfiles? Reason being, I love the way your emacs looks.

  • @dimak7769
    @dimak7769 Год назад

    Tnanks, just awesome!!! VERY IMPORTANT QUESTIONS:
    1. What is the name of your VS Code theme?
    2. How did you make highlight the current line number?

    • @antifa_communist
      @antifa_communist 7 месяцев назад

      It's not VSCode, it's doom emacs. Not sure exactly what that is, but he said it in another comment.

  • @alfredomenezes8814
    @alfredomenezes8814 Год назад +1

    Very nice video, I love your Rust content, keep going!

  • @Bvngee
    @Bvngee Год назад

    Thanks for this! thought I knew the basics already. I didn't even know that &impl Trait existed, and I'm not even finished watching yet.

  • @DaddyFrosty
    @DaddyFrosty Год назад +2

    Not sure what the curve ball was but I do think that it’s great that rust lets you generate templated versions of functions. But at the end of the day a VTable lookup isn’t the end of the world.

    • @codetothemoon
      @codetothemoon  Год назад +1

      The curve ball was that you can't just use a trait as a parameter type, you have to specify 'dyn' or 'impl'. In most languages that's a decision that you don't have to make.

    • @DaddyFrosty
      @DaddyFrosty Год назад +1

      @@codetothemoonfair enough 👍

  • @collynchristopherbrenner3245
    @collynchristopherbrenner3245 2 месяца назад

    First time learning about traits in Rust after hearing such raving reviews from ThePrimeagen. And wow, how cool! This feels like the way polymorphism was meant to be done all along.
    It is a little strange that you couldn't put all three traits on the same line though. Now I'm wondering about the requirement of declaring before implementing or if they can be done at the same time. I already like Rust based on this one video.

  • @ericleijonmarck
    @ericleijonmarck Год назад +2

    I love your videos, I like when you insert some images/videos to make it clear what you mean by landvehicle for instance. It is just a bit of alot of images switches in the video.

    • @codetothemoon
      @codetothemoon  Год назад

      thanks for the feedback, we are always trying to make things entertaining while not being too obnoxious, sometimes that's a fine line to traverse!

  • @ReedoTV
    @ReedoTV Год назад +6

    Had no idea super traits were a thing!

    • @codetothemoon
      @codetothemoon  Год назад +4

      nice, love it when folks who already know about the video topic are still able to get value!

    • @ReedoTV
      @ReedoTV Год назад +3

      @@codetothemoon It's hard to find intermediate level content on Rust, so thank you for all the videos you make!
      I'm finding that going from being adequate at Rust to being good at it is a very long journey.

  • @JosephDalrymple
    @JosephDalrymple Год назад +3

    Building my first REST API using axum and sqlx, and I wanted to use `traits` in order to be able to swap out instances of certain aspects for their mock equivalents. Unfortunately, since so much of the code requires `async`, this proved to be impossible. I know in the nightly compiler there's _some_ support for async trait members, but I'm honestly more interested in getting myself to a place where I can optimize away most of the `async` to make things more testable.
    I started my career in C/C++, and then moved to Front End in the latter portion of my career, where I've become addicted to 80% Test Coverage minimums in the professional space, and 100% test coverage minimums in my personal projects. This has proved to be _quite_ the exercise moving back to strongly typed languages haha: Almost exclusively due to mocking.
    I'm a huge advocate of simple input-output and keeping most functions as simple as possible in order to make them easier to test. In the frontend space, it's super easy to swap out a struct with something that _looks_ the same, but functionally isn't. This has proven quite the task in Rust 😂

    • @JosephDalrymple
      @JosephDalrymple Год назад +1

      I see more and more "Integration-style Tests" in Rust and C/C++ code bases than Unit Tests, which I feel adds complications which cloud the purpose of the function and the tests, making the code less stable overall.

    • @levizin917
      @levizin917 Год назад

      If you dive into rust for pratical uses you need to turn everything into async, so don't worry about that and do use async traits, also traits are really useful for mocking and testing and my company uses it all the time. But also makes so your struct has this contract (the trait) that tells you what it needs to implemento to be used for your porpose. This way you can always add new providers or mantain your code using this trait that tells you what your code needs to do. Here is one example:
      Imagine your service will hire a payments api service, and you build a Client for it that is able to call all the api's of the service. This endpoints are from your provider and there is no personal logic of yours. Now you need to not only call a endpoint, but multiple, or transform data before using it. This logic will go in your trait PaymentProvider or something like that. Another thing that can be useful for doing this is. Imagine that this Client struct is used by many services in your company. But your service only use a few of this endpoints. In your personal service trait you will only specify those endpoints that you need. My point is that this traits that are placed for mocking will be used for other reasons

    • @levizin917
      @levizin917 Год назад

      @@JosephDalrymple About this integration-style tests I can say that usualy mocking is always the way for calling other services or providers and only testing integration with your database is useful, which always is easy to replicate for testing and doesn't make it unstable

    • @JosephDalrymple
      @JosephDalrymple Год назад

      ​@@levizin917 Unfortunately, spinning up, populating, and spinning down a database tends to be heavy, and shouldn't be done as a regular test of your codes functionality. These tests are great because they're End-To-End, but Unit Tests should make up the bulk of tests for any given application.
      Unit Tests typically test a single function at a time. But this can be difficult when async traits are not yet fully supported in Rust. While there are workarounds in the meantime, the problem in and of itself is already quite a complex one to solve, given the need to calculate lifetimes beyond what is currently done for structs with standard methods. Because of this, its not a trivial task to pivot from concrete async structs to dynamic async traits, especially when the only gain comes in the testing world.
      Most of what I've done to alleviate that is to create #[cfg(test)] and #[cfg(not(test))] implementations of my structs, which is a major pain, but it gets around having to deal with the immense overhead of introducing modular functionality just for testing purposes, when there isn't much else to gain.
      A more attractive solution would be the ability to define a mod.mock.rs or something to define the mock implementation of a struct for testing purpose. It's just not worth the overhead to introduce additional heap allocations simply to make testing more intuitive. That has unnecessary real-world implications on your application's performance.

    • @levizin917
      @levizin917 Год назад

      @@JosephDalrymple I can only atest to how we do things at my job, whe usually use traits with async_trait library, and mock with mockall for unit testing. the services dont have any problem and no problems in performance.

  • @2002budokan
    @2002budokan Год назад +2

    Excellent small examples.

  • @Xerofull
    @Xerofull Год назад

    I like your video style, its getting better with each one
    +1

  • @ozanmuyes
    @ozanmuyes Год назад +1

    At 5:20 you said "Rust" which is lol because I'm here to learn about it 😀 Jokes aside good bite sized tuts, cheers

  • @janbaumann7794
    @janbaumann7794 Год назад +1

    Really appreciate these videos. Keep up the good work :)

    • @codetothemoon
      @codetothemoon  Год назад

      wow thank you so much for the support Jan!!! more to come!

  • @rainerwahnsinn3262
    @rainerwahnsinn3262 Год назад

    I think you should have mentioned that `` is the normal syntax for a trait bound, and `impl MyTrait` is just a shorthand with the limitation that you can't reuse it in multiple places, e.g. as both argument and return type.

  • @DrumMeister
    @DrumMeister Год назад +3

    Something which is weird is that the compiler gives ‘dyn’ as solution instead of the faster ‘impl’. That’s most likely because generics is harder to use for devs? Or what is the reason?

    • @codetothemoon
      @codetothemoon  Год назад +2

      I was wondering about this as well, I'm actually not sure!

    • @DrumMeister
      @DrumMeister Год назад

      @@codetothemoon impressive answer from chatgpt 😜:
      “The Rust compiler does not necessarily prefer dyn over impl in function signatures. The use of dyn and impl has different implications and may result in better or worse outcomes depending on the situation.
      In general, impl is suitable for specifying the exact implementation details of a type, while dyn is suitable for specifying a trait object that exhibits a certain behavior but whose exact implementation is not important.
      In function signatures, the use of dyn or impl may depend on the exact details of the function and the user's needs. If a function requires a specific type, and its implementation is important for the function, impl may be more appropriate. However, if the function requires a certain behavior that can be provided by multiple types, dyn may be more suitable because it provides the user with more flexibility in the choice of type.
      It is important to understand the implications of impl and dyn and base the choice on the specific needs of the situation. In some cases, the use of impl may result in better outcomes, while in other cases, dyn may be more suitable.”

  • @TheToric
    @TheToric Год назад +1

    You are correct in that rust does not consider itself object orienyed, as rust consideres object orientation an antipattern. Rust olis functional/procedural.

    • @codetothemoon
      @codetothemoon  Год назад +1

      Some would argue that using polymorphism is "object oriented programming". I think the lines are a bit gray here.

    • @bradywb98
      @bradywb98 Год назад

      “Objects” are no different than structs in Rust. You’ve got a memory region with a set of data in it. You’ve got a collection of operations the compiler will allow you to perform on that region. There is nothing special about objects.

    • @bradywb98
      @bradywb98 Год назад

      The conversation behind classes and inheritance is different. But to associate “objects” with those ideas is IMO wrong.

  • @HiltonFernandes
    @HiltonFernandes 5 месяцев назад +1

    Great explanation. Congrats !

  • @oglothenerd
    @oglothenerd 4 месяца назад +3

    0:20 Why... why did you include that clip? My nerves are on fire.

    • @codetothemoon
      @codetothemoon  4 месяца назад +1

      hah! it may have had the desired effect 🙃

    • @oglothenerd
      @oglothenerd 4 месяца назад +1

      @@codetothemoon Oh you little...

  • @hos7012
    @hos7012 Год назад +2

    thx for your effort
    what keyboard and IDE do u use?

    • @codetothemoon
      @codetothemoon  Год назад +1

      thanks for watching! Keyboard is Corne v3 and IDE is DOOM Emacs

  • @goodwish1543
    @goodwish1543 Год назад

    Easy to read. May I ask a question: what's your workaround on Trait object with generic function parameter? Why this kind of Trait object is unsafe?

  • @АртемФедоров-ю7б
    @АртемФедоров-ю7б 10 месяцев назад

    This is common use case when you need to implement some behaviour which uses some data + also you want to have some structs sharing some field sets. Instead you invent a wheel in rust to achieve that, by implementing getters/setters to use them in default implementation

  • @barefeg
    @barefeg Год назад +2

    Would it be possible to implement amphibious directly without having to implement lang capable and water capable?

  • @johnbaxter7582
    @johnbaxter7582 Год назад +1

    cool quick tutorial, just found out about dyn and impl

    • @codetothemoon
      @codetothemoon  Год назад

      thanks glad you got something out of it!

  • @YuruCampSupermacy
    @YuruCampSupermacy Год назад +1

    loved the video, keep making more

  • @MTrax
    @MTrax 8 месяцев назад

    Gosh I really thought you were the typing god himself until I realized that it’s sped up

  • @nsgirish
    @nsgirish Год назад +1

    excellently explained

  • @jrmoulton
    @jrmoulton Год назад +3

    Good video. Why the switch to emacs? Also random note in case you've never seen it, Control-l will clear the terminal screen without having to type clear.

    • @codetothemoon
      @codetothemoon  Год назад +2

      thanks! I had been using VSCode just for the videos because it seemed like thats what most of the viewers are using, while using neovim behind the scenes. I switched to doom emacs and decided I really liked it, and haven't felt especially compelled to switch back to neovim. Shortly thereafter I decided to try using emacs in the videos as I figured it probably would't bother the VSCode folks - hopefully that is indeed the case 🙃
      Thanks for the Ctrl-I tip, I'm constantly finding gaping holes like this in my knowledge of shortcuts!

    • @zzzyyyxxx
      @zzzyyyxxx Год назад +5

      @@codetothemoon only the code matters, not the editor you use, so keep using whatever editor you'd like, doesn't bother us

    • @jrmoulton
      @jrmoulton Год назад +1

      @@codetothemoon Makes sense. I think they've all looked great in the videos.

  • @JosephDalrymple
    @JosephDalrymple Год назад

    I'm curious if the Rust community would be interested in the capability to Copy instead of using Fat Pointers. In other words, allow the compiler to be modified in such a way that the project you're building (maybe not the dependencies, since they weren't built with that option) to choose where the cost is incurred.
    Granted, that would essentially turn Traits into a type of Generics.

  • @delir0
    @delir0 Год назад +1

    Have anyone mentioned the most "Rusted" variant for the function? fn road_trip(vehicle: &T). This also allows us to make the function traverse_frozen_lake without making any "aggregation" traits (Amphibious). Just like that: fn road_trip(vehicle: &T)

  • @jacekm.1789
    @jacekm.1789 8 месяцев назад +1

    amazing video. thank you so much!

    • @codetothemoon
      @codetothemoon  8 месяцев назад

      thanks, really happy you liked it!

  • @xenocampanoli815
    @xenocampanoli815 11 месяцев назад

    Looking all around for examples of a Vec or other collection contains a bunch of different subtraits, which in your case would be vehicles. Vec. Temporarily stuck on this.

    • @haraldbackfisch1981
      @haraldbackfisch1981 8 месяцев назад

      I know this is an old question but I had to do sth similar lately. I just created an enum which variants contain the things with different traits.
      Then you can have a vector of this enum and can match over the enum to get to the desired element (subtype).

  • @pixelstriko1642
    @pixelstriko1642 Год назад +1

    As soon as i saw you copy pasting the drive method. I screamed 'USE A TRAIT!!'

  • @chris.davidoff
    @chris.davidoff Год назад +1

    I'm really curious what the performance difference of impl vs dyn really is.
    I suppose this would be a good mini-challenge/project for me to do :P

    • @codetothemoon
      @codetothemoon  Год назад

      yeah, it would be an interesting test - i'd be shocked if nobody has posted the results of such a test yet...

  • @bradywb98
    @bradywb98 Год назад

    I don’t think having to annotate parameters as being “dyn” is an example of a ZCA. This is just the compiler requiring you to… actually specify the parameter type. A trait is not a type, it’s just a constraint. Types describe the structure of some set of data of memory. Traits are just descriptions of the operations you can perform on some unknown set of data.
    The data being passed as the argument is completely different for dyn and non-dyn parameters. The former is a fat pointer to a vtable and to an object. The latter is the full set of data (or a pointer to it if we’re using references).
    Hence these are different types. “dyn” is a type qualifier just like “mut” is and the compiler is not being overly picky by requiring you to use it.

  • @adicide9070
    @adicide9070 Год назад

    I think I missed the twist :D was it the impl vs dyn? :)

  • @GlobalYoung7
    @GlobalYoung7 Год назад +1

    thank you 👍⚡️

  • @befosocial
    @befosocial Год назад +1

    I really thought Emacs has gone extinct, but it seems to have one last user.

    • @codetothemoon
      @codetothemoon  Год назад

      Lol 😂 I’m definately not one of those folks who is married to their editor, give me an alternative that has org mode and supports multiple fonts in the same buffer and I’ll consider switching 🙃

    • @befosocial
      @befosocial Год назад +1

      @@codetothemoon I definitely am one of those, but I've chosen the only good and bright side there is. 🤣

    • @codetothemoon
      @codetothemoon  Год назад

      @@befosocial you're referring to Notepad I'm assuming? 😎

  • @Taurdil
    @Taurdil Год назад +1

    What kind of dynamic dispatch does Rust have though? Single(as Java, C++,C#, etc) or Multiple (CLisp) ? I mean, say we now have super trait "Surface" and "Road" and "Ice" extends it. Then 'drive' would take "&dyn Surface" and, say, would print "Driving/Floating on {surface.name()}".

    • @mk72v2oq
      @mk72v2oq Год назад

      Here is the test, if I got it right.
      Code:
      #![feature(trait_upcasting)]
      trait Surface {
      fn name(&self) -> &str {
      "unknown surface"
      }
      }
      trait Road: Surface {
      fn name(&self) -> &str {
      "road"
      }
      }
      trait Ice: Surface {
      fn name(&self) -> &str {
      "ice"
      }
      }
      struct Street {}
      impl Surface for Street {}
      impl Road for Street {}
      struct Glacier {}
      impl Surface for Glacier {}
      impl Ice for Glacier {}
      fn print(surface: &dyn Surface) {
      println!("Driving/Floating on {}.", surface.name());
      }
      fn main() {
      print(&Street {} as &dyn Road);
      print(&Glacier {} as &dyn Ice);
      }
      Output:
      Driving/Floating on unknown surface.
      Driving/Floating on unknown surface.

    • @biskitpagla
      @biskitpagla Год назад

      Rust has single dispatch, an influence from C++. Bjarne has written at length about the difficulties of implementing multiple dispatch in a systems language.

  • @JacobNax
    @JacobNax 5 месяцев назад

    Inheritance is such a pain in the ass... I'm so glad i started learning Rust and moving away from foolery (oop, nulls)

  • @GlobalYoung7
    @GlobalYoung7 Год назад +1

    thank you

    • @codetothemoon
      @codetothemoon  Год назад

      wow thank you so much for your support!!!!

  • @renxula
    @renxula Год назад +1

    Woah, how does your "cargo run" compile it in 0.11s? I just got mine to compile a trivial program in 2.5s instead of 4.5s by changing the linker. (This was in WSL2.) Now that I know it CAN be fast, I want to make it that fast! 2 seconds is long enough to get bored...

    • @codetothemoon
      @codetothemoon  Год назад

      Not sure, I haven't done anything to deliberately make it faster. I am on a 1 year old M1 macbook pro, not sure if it's just because of the hardware

    • @renxula
      @renxula Год назад +1

      @@codetothemoon Thanks anyway - when I saw that compilation can be fast, I went searching for answers. It turns out that WSL2 was indeed my problem: accessing files from the NTFS side is quite slow. (It doesn't normally feel THAT slow, but I guess cargo or rustc accesses a LOT of files.) The solution is to move the source files to the Linux filesystem, or run the Rust compiler on Windows. VS Code can edit the files inside WSL, too.
      Btw, here's a hint for you, when you use rust-analyzer with VS Code: it shows the inferred types of local variables by default, but those can be hidden, as well as function parameter names. I find those to be spammy and super distracting, and I can get the same info if I need it, by hovering over the text :)

  • @jwr6796
    @jwr6796 Месяц назад +1

    So is it correct to say that the trade-off between DYN and IMPL is a little bit of performance versus a little bit of extra binary size?

    • @codetothemoon
      @codetothemoon  Месяц назад +1

      That's usually the tradeoff, at least in the case of function parameters. But are situations where you might be forced to use dyn, as in the case where you'd like to make a vector of things for which you don't know the concrete type. And then I believe in the embedded world where there might not be a heap, you may be forced to use impl instead of dyn as well.

    • @jwr6796
      @jwr6796 Месяц назад

      @@codetothemoon thanks! 👍

  • @mvargasmoran
    @mvargasmoran Год назад +1

    like your style.

  • @Erhannis
    @Erhannis Год назад

    I emitted quite a strange noise at the clip art of somebody washing a circuit board

  • @michaelsohnen6526
    @michaelsohnen6526 Год назад

    What is the difference between static dispatch and generics with trait bounds? Im a beginner and have never even seen the use of 'impl' in a rust function signature.

  • @raphaeljaggerd3585
    @raphaeljaggerd3585 Год назад +1

    Its like inheritance had composition had a baby but composition had stronger genetics.

    • @codetothemoon
      @codetothemoon  Год назад +1

      LOL that's a very good description of the approach Rust takes 😎

    • @raphaeljaggerd3585
      @raphaeljaggerd3585 Год назад

      @@codetothemoon I'm not a rust developer but i just realized ive been subconsciouslly learning Rust through your videos. It now feels less intimidating, thank you so much for sharing your knowledge with us.

  • @TheLetsComment
    @TheLetsComment Год назад

    7:55 you could simply use a Trait Alias, since the Amphibious Trait has no methods (trait Amphibious = LandCapable + WaterCapable;)

  • @lautaroblasco
    @lautaroblasco Год назад +1

    Good video

  • @guilherme5094
    @guilherme5094 Год назад +1

    👍Thanks!

  • @RichardBurgess-v2h
    @RichardBurgess-v2h 11 месяцев назад

    Is there a problem putting a function inside an impl block without a "self" in order to associate it with that group, kind of like a class method would be? How would I otherwise implement something that is like a class method?

  • @rya-q6e
    @rya-q6e Год назад +1

    very nice

  • @razvbir
    @razvbir Год назад +1

    Took me a while to notice that you are using doom emacs

    • @codetothemoon
      @codetothemoon  Год назад

      indeed, I've been pretty happy with it lately

    • @razvbir
      @razvbir Год назад

      @@codetothemoon Your video on helix made me try it. Any plans to cover doom emacs in a future video?

  • @persianmaster400
    @persianmaster400 Год назад

    It seems to me adding impl WaterCapable and impl LandCapable for Hovercraft is redundant since the Hovercraft is Amphibious which is defined already as both WaterCapable and LandCapable. Could you please explain why we still need to add those?

    • @digama0
      @digama0 10 месяцев назад

      The supertrait bound only means that any type which implements Ambhibious is required to also implement WaterCapable and LandCapable, it doesn't do the implementation for you (not least because the implementation usually has functions in it which you are supposed to write). If you wanted to get an automatic implementation, you would not have "trait Ambhibious: WaterCapable + LandCapable" but instead have "impl WaterCapable for T { ... }", and this implementation would be used even if you only have "impl Ambhibious for Foo". But beware, if you do this then you will not be allowed to also write "impl WaterCapable for Foo", as this would result in two implementations of WaterCapable for Foo and rust doesn't like that.

  • @nirajgautam403
    @nirajgautam403 9 месяцев назад

    Can we use vars from base struct in derived one?

  • @rGxDale
    @rGxDale Год назад

    What editor and theme is that? its fkin beautiful

    • @bhavyakukkar
      @bhavyakukkar 10 месяцев назад +1

      doom emacs, monokai-classic

  • @anotherelvis
    @anotherelvis 3 месяца назад +1

    I would prefer to name the trait Drive, so that the trait name is the same as the provided method.

    • @codetothemoon
      @codetothemoon  3 месяца назад +1

      ahh great suggestion, I think I'd like that better as well.

  • @football-is-divine
    @football-is-divine 10 месяцев назад

    Which theme is that? 😮

  • @larrymarso4492
    @larrymarso4492 Год назад

    Very nice! Now do trait objects.

  • @handsanitizer2457
    @handsanitizer2457 Год назад +2

    2nd interesting thanks

  • @bombrman1994
    @bombrman1994 8 месяцев назад

    providing code would be nice

  • @Onyx-it8gk
    @Onyx-it8gk 3 месяца назад +2

    It's so frustrating that when people try to explain OOP-like features, they always use these super obscure examples like dogs barking and cars driving 🙄 Please just use concrete examples we're likely to encounter

  • @samuelhurel6402
    @samuelhurel6402 Год назад

    If the lake is frozen, why would we need to float ? ;p

  • @aryabp
    @aryabp Год назад

    bruh i tought rust now driven by NATO

  • @RogasTV
    @RogasTV Год назад

    Please consider facilitating object oriented programming in other languages. Rust should be used in a more functional style.
    Having said that, it was a very good explanation! Thank you!
    Btw, C++ does not have generics.

    • @biskitpagla
      @biskitpagla Год назад +1

      Rust's traits system *is* based on some OO ideas. I don't understand what you're trying to achieve by abstaining from Rust's premier construct for polymorphism. The language is clearly multiparadigm and you're only going to end up writing non-idiomatic code if you're in denial of that.
      Your comment about C++ also doesn't make sense. A project like STL was authored in C++ specifically because it had the best generics support you could get in a systems language at that time, even though STL's author preferred functional over the other paradigms.
      STL is also based on OO ideas, excluding inheritence. Of the OO ideas Rust took from C++, an STL inspired standard library and traditional oo single dispatch, which the traits system uses, come to mind. Rust is oo for the same reason C++ is, it just makes sense to encapsulation remaining state when you're done with the functional and pure part of your program. This is a natural evolution of systems languages that also target the application realm. I don't think there's any value in denying this.

    • @bradywb98
      @bradywb98 Год назад

      The hive mind doesn’t know what they’re talking about at this point. What is special about an “object” in C++ vs a struct instance in Rust? Answer: nothing. An object is just a set of data in memory that can have certain operations performed on it. A struct instance if just a set of data in memory that can have certain operations performed on it. There is nothing special about “objects”.
      Using polymorphism has nothing to do with “object orientation”. Rust’s enums, traits, and generics all work with “objects” but those are premier features of the language.
      You can’t write any code without having complex structured sets of data and defined operations for them. These are objects. People see “OOP” and “Rust” in the same sentence and their brain cells dissolve.

  • @verified_tinker1818
    @verified_tinker1818 Год назад +1

    Since when is Rust object-oriented? I thought it was a procedural language with functional bits.

    • @peter9477
      @peter9477 Год назад +2

      It's not a class-based OOP language, but that's not an absolute requirement to be considered OO.

  • @headlibrarian1996
    @headlibrarian1996 5 месяцев назад

    Those empty impl blocks for composed traits seem dumb.

  • @DJenriqez
    @DJenriqez 5 месяцев назад

    Now be a man and show how to cast elements in HashMap where there are 3 types in one map, Entity, Station, Buffer,... types can interact with each other... so you have to cast them to specific type.

  • @brunomello7499
    @brunomello7499 Год назад +1

    You type hella fast

    • @codetothemoon
      @codetothemoon  Год назад +1

      that fast forward makes me look like a competent typer!

  • @ErikBongers
    @ErikBongers Год назад

    My main takeaway....English doesn't have a general verb to describe a travelling boat! There's 'to sail', but nothing more general. Luckily Nelson died before having to command engine driven boats. Or did he?

  • @justsomerandomguyman
    @justsomerandomguyman Год назад +1

    But.... Rust isn't object oriented, and you shouldn't treat it as such, nor do you need it to be.

    • @codetothemoon
      @codetothemoon  Год назад

      What is your definition of object oriented?

  • @NikolajLepka
    @NikolajLepka Год назад +2

    ctrl+l (L not i) clears the terminal. you don't have to type clear in full

    • @codetothemoon
      @codetothemoon  Год назад +1

      thanks for the tip! i have some gaping holes in my knowledge of shortcuts. Getting tips like this is one of the things I like most about uploading videos 😎