Stop Using Object Oriented Programming In Rust (Do This Instead)

Поділитися
Вставка
  • Опубліковано 29 гру 2024

КОМЕНТАРІ • 99

  • @morgomi
    @morgomi 4 місяці тому +12

    by the way I really liked that concept (reading nice articles together.) 🙂

    • @dario.lencina
      @dario.lencina  4 місяці тому +2

      Dude this is very insightful feedback!! I will do it much more often!!! 🏃‍♀️🏃🏃‍♂️💨

    • @MuscleTeamOfficial
      @MuscleTeamOfficial 4 місяці тому +2

      @@dario.lencina yeah, I need more ppl to read articles for my lazy butt

    • @dario.lencina
      @dario.lencina  4 місяці тому

      Hahaha!!

    • @collinsmarra
      @collinsmarra 4 місяці тому +1

      @@dario.lencina Yes, waiting

    • @morgomi
      @morgomi 4 місяці тому +1

      @@MuscleTeamOfficial exactly 🤣

  • @codeman99-dev
    @codeman99-dev 4 місяці тому +57

    What is going on with your camera zoom?! Super distracting.

    • @dario.lencina
      @dario.lencina  4 місяці тому

      Hey! Do you have motion sickness while playing games? I actually really like it 😄

    • @moose6459
      @moose6459 2 місяці тому +16

      @@dario.lencinait would be one thing if it zoomed in on certain key words, but it looks like it just randomly zooms in and out.
      I agree, it’s distracting and looks shit.

    • @dario.lencina
      @dario.lencina  2 місяці тому +1

      @moose6459 hahaha XD got it

    • @arthurschur
      @arthurschur Місяць тому +4

      @@dario.lencinaits feedback!😄

    • @dario.lencina
      @dario.lencina  Місяць тому +2

      That is true 😄

  • @LoZander
    @LoZander Місяць тому +5

    if youre in a situation where you really want to do the trait/interface thing, you could make the function polymorohic like
    fn speak(x: T)
    (i believe the notation is something like that)
    or equivalently
    fn speak(x: impl Animal)
    What happens is that rust will create a function for each actual type it's called on, and this happens at compile time, meaning there is no dynamic dispatch shenanigans. This kinda polymorphism is resolved at compile time which makes it a go to for me over boxing dyns which uses dynamic dispatch at runtime.
    But yeah, in the example in the video, just using an enum works

  • @edinalewis4704
    @edinalewis4704 2 місяці тому +14

    OOP != Inheritance.

    • @dario.lencina
      @dario.lencina  12 днів тому

      Yup I agree. Rust does have oop via composition with traits 👍

  • @DevRJPro
    @DevRJPro 4 місяці тому +10

    This method is more efficient, but has the disadvantage of requiring a "Speak" overlay in the Animal "enum" with a "match".

    • @dario.lencina
      @dario.lencina  4 місяці тому +1

      Yes! So the code needs a match statement, do you think that is bad?

    • @DevRJPro
      @DevRJPro 4 місяці тому +4

      ​@@dario.lencina
      If the enum contains several elements, the match will have to follow, and this will create a strong coupling. So the choice will be between cleaner, more maintainable code, or more powerful, strongly typed code.

    • @dario.lencina
      @dario.lencina  4 місяці тому +1

      @DevRJPro yup there’s a balance between the two, can’t have your cake and eat it too 👍

    • @stdprocedure
      @stdprocedure 2 місяці тому +1

      @@DevRJPro a proc macro would solve the boilerplate issue ig

    • @Miles-co5xm
      @Miles-co5xm Місяць тому +1

      @@dario.lencina What if this is crate and i want use to implement his own structure, Hoow will user add it to enum/

  • @wadsaek2290
    @wadsaek2290 4 місяці тому +10

    but what if you wanted to expose the zoo? like, if someone external user had wanted to declare a new animal(e.g. a duck). this isn't possible with an enum
    also all the animals will still be stored on the heap, since it's a vector and vectors allocate heap memory to allow for dynamic size. there is still an advantage: all you data is stored in sequence, and not via pointers to some random heap memory, but that's not what you said

    • @dario.lencina
      @dario.lencina  4 місяці тому +2

      I see! Do you save a degree of indirection by storing the objects into the vector instead of a vector of box pointers?
      Yeah if there’s no way to avoid Box sure then I would use it, but many systems do not need this.
      Thanks for your feedback!!

    • @wadsaek2290
      @wadsaek2290 4 місяці тому

      @@dario.lencinathe part of the Vector that is on the stack is basically a fat pointer to an array on the heap, so you need go to follow it to find the data, so that's one degree of indirection. when you have a vector of pointers, you have to firstly follow the pointer to the array and then the pointer to the needed data

    • @dario.lencina
      @dario.lencina  4 місяці тому +2

      Yup that is what I am saying, you will have a contiguous block of memory on the heap, so there’s a significant saving by using Vec instead of Vec particularly if you are constantly mutating the vector

    • @dario.lencina
      @dario.lencina  4 місяці тому

      Hey!! I found a way to clip the video and remove the heap comment and just focus on the aspect, I appreciate your feedback!!

    • @lazarpro3030
      @lazarpro3030 Місяць тому

      @@dario.lencina I recently had a very similar problem where i had Arc, i decided to throw it in the heap and be done so Arc, keep in mind that i can't use an enum because i don't know the structs which will implement the trait.

  • @ClimateChangeDoesntBargain
    @ClimateChangeDoesntBargain 3 місяці тому +6

    Another way: Just create two vectors with one type of animal each -> no matching, no boxing, no enums, no traits

    • @dario.lencina
      @dario.lencina  3 місяці тому +1

      😱😱😱nice!!

    • @jaysistar2711
      @jaysistar2711 Місяць тому +1

      The struct of arrays pattern is bordering on ECS, which is probably the best choice for a simulation. You can have Animal objects in an array, and extra data for each specific animal can go in a sparce storage container, like BTreeMap or HashMap whose keys are indexes into the Animal array (Vec). The enum in the video makes an assumption that something will never have parts of 2 animal types, and that each variant is about the same size. If you add a large variant, you make each Vec element big enough to hold the largest variant, even though you don't need it. You also have to have the discriminant as part of the Vec element, as well.

  • @h.s3187
    @h.s3187 4 місяці тому +5

    The problem of OOP is that there is no standard way of sintax implementation , simula tried to do this but iwhat happened is that we have completely different abstractions in different languages .

  • @JavierHarford
    @JavierHarford 4 місяці тому +3

    Also please post the article URL that you were reading please

    • @dario.lencina
      @dario.lencina  4 місяці тому +1

      here you go my friend: courses.grainger.illinois.edu/cs225/fa2022/resources/stack-heap/

  • @CalifornianViking
    @CalifornianViking Місяць тому +1

    Thanks, Dario - this is an example of "just because you can do something, it does not mean that you should do something".
    I find that too many "advanced" examples in Rust use Box. I have always felt uncomfortable about it. It could just be that I am not experienced enough, but it feels like there are a lot of hidden side effects.
    I have seen people using Box to return any error, but now the caller has to implement complex code to understand the error.
    Rust has an elegant way of passing variables using traits, it is called generics. This allows the user to pass and return objects without specifying their exact type ad without using Box.

    • @dario.lencina
      @dario.lencina  Місяць тому

      Exactly! So many times you can actually avoid it. With that said, sometimes you cannot avoid the box and that is fine too

  • @ok-alarm
    @ok-alarm 3 місяці тому +2

    is this good practice?
    fn sample(a: u8, b: u8) -> Result {}

    • @dario.lencina
      @dario.lencina  3 місяці тому +1

      Why do you need the Box for the error? Is there a way for you to know what the error is going to be at compile time?

  • @kaiserbergin
    @kaiserbergin Місяць тому +1

    Good vid, liked the examples. Great example of how to convert traditional OOP habits from other languages to something more rusty! Now turn off the zoom lol.

  • @gman8361
    @gman8361 3 місяці тому +1

    Thanks for the info! I don't think I have ever been this excited to learn a new language. I currently do web development using PHP and JavaScript. I have dabbled with C, C#, and C++ a little bit, but never got to far with them. So far Rust is holding my attention.

    • @dario.lencina
      @dario.lencina  3 місяці тому

      Whoah !! Welcome to the party gman!! Another thing, it is fine to use Box dyn Trait if you don’t have access to all the structs that will be using your lib! This video only applies to code that you control 100%

  • @antifa_communist
    @antifa_communist 19 днів тому +1

    Please make more videos like this one

  • @JoelPasapera
    @JoelPasapera 29 днів тому +1

    me gustaria que rust tuviera un entorno de programación orientada a objetos

    • @dario.lencina
      @dario.lencina  12 днів тому

      Hey Joel! De hecho si lo tiene! Checa este capítulo doc.rust-lang.org/book/ch17-00-oop.html

    • @JoelPasapera
      @JoelPasapera 12 днів тому +1

      @@dario.lencina Muchas gracias, deseo crear librerias en Rust para luego usarlas en Python, estoy aprendiendo Rust. Gracias por el contenido

    • @dario.lencina
      @dario.lencina  12 днів тому

      Si! Es una gran manera de mejorar el performance de tu aplicación en Python. Ya viste mi video en maturin?

  • @kuqmua755
    @kuqmua755 4 місяці тому

    Is it a bad practice if first variant is large and second variant is small in memory?

    • @dario.lencina
      @dario.lencina  4 місяці тому

      @@kuqmua755 no need to worry at all! The impact in performance is negligible

  • @shiftRightOnce
    @shiftRightOnce 4 місяці тому +4

    Your example assumes you know all of the "kind" of animals before hand. In that case yes, there is no need for box . However, trait can be use as a contract where the implementor is unknown. Telling developers to STOP doing X or Y comes across as authoritative. In such a case cover all scenarios covered by your "STOP" doing..

    • @dario.lencina
      @dario.lencina  4 місяці тому +1

      This is fair, indeed you should know all your variants to prevent the Box what is your point?
      How would you change the title? If you know all the variants then you can avoid the dyn? We have 60 characters and a thumbnail to convey complex ideas 💡 it is pretty tough bud.

    • @shiftRightOnce
      @shiftRightOnce 4 місяці тому +3

      @@dario.lencina Something on the line of "Another way to avoid using Box" or "Alternative to Box"

    • @dario.lencina
      @dario.lencina  4 місяці тому +1

      Ok I will think harder about how to name these things moving forward, thanks for your feedback ✅👍

  • @DarkShroom
    @DarkShroom 3 дні тому +1

    you're still using objects...structures are still objects

    • @dario.lencina
      @dario.lencina  2 дні тому

      You got that right, I am playing with the fact that most people think about inheritance as a core concept in oop vs composition. In fact and to your point the rust book has a whole chapter on oop

  • @morgomi
    @morgomi 4 місяці тому +1

    insightful! thank you!

    • @dario.lencina
      @dario.lencina  4 місяці тому +1

      thanks Enes! I am going to go much harder on Rust moving forward my friend!!

  • @Seacrest.
    @Seacrest. 12 днів тому +1

    yes! just KISS of code

  • @JavierHarford
    @JavierHarford 4 місяці тому +2

    Keeping watch for your next videos, this was a good one thanks! More Anti-Javascript brain videos please!

    • @dario.lencina
      @dario.lencina  4 місяці тому

      Thanks Javier!! It’s been challenging for me to get rid of some js bad habits and it makes me super happy that this helped you too!!

    • @okie9025
      @okie9025 4 місяці тому +2

      wtf is javascript brain?

    • @JavierHarford
      @JavierHarford 4 місяці тому

      ​@@okie9025 Javascript brain is when your main language is all loosey goosey about types, and garbage collected heavily so you never learn to avoid footguns because the interpreter / jit compiler wipe your butt and change your nappies. When you start programming with a language more close to the metal, you keep these bad habits leading to ass whoopings by the rust compiler for being unruly

    • @okie9025
      @okie9025 4 місяці тому +1

      ​​@@JavierHarfordlol now that's just childish. I'd argue rust brain is a more terminal condition than "javascript brain", with the mindless cult following behind it and all. Also most people use typescript nowadays, and I don't think you know what a footgun is. Also "ass whoopings by the rust compiler"? LMAO if any compiler is baby oriented it's rustc. Rust isn't hard, it just tries to be too many things at once.

    • @dario.lencina
      @dario.lencina  4 місяці тому

      @@okie9025 is when you want to force JavaScript design pattern into other languages

  • @ІванБоровик-э8л
    @ІванБоровик-э8л 4 місяці тому +1

    Won't it take more memory, because of the enum elements size?

    • @pointlessone3702
      @pointlessone3702 4 місяці тому +1

      The size of enum is the size of the biggest variant + size of usize for the tag (if there's more than one variant in the enum).

    • @jcm2606
      @jcm2606 2 місяці тому

      @@pointlessone3702 There's also extra padding bytes to account for the alignment. For instance, if you had an `Option` or an equivalent, this would take up 32 bytes since it's 4 bytes for the tag, 12 bytes of padding, then 16 bytes for the `u128` value, since that `u128` value _has_ to exist at a memory address that is a multiple of 16. There are some cases where both the tag and the padding bytes aren't needed due to the presence of a niche value, such as when you have an `Option` (the inner `u128` value cannot be a 0, so `Option` can use 0 to represent the `None` variant) or when you have an `Option` (references cannot be null, so `Option` can use the null address to represent the `None` variant), but in most cases there will be extra padding bytes.

  • @adicide9070
    @adicide9070 Місяць тому +1

    and Dog(Dog)Cat(Cat) ain't ugly? mmmmmhm.

    • @dario.lencina
      @dario.lencina  Місяць тому +1

      Well, in retrospect… yes!!

    • @adicide9070
      @adicide9070 Місяць тому

      @@dario.lencina there ought to be a guide to non-ugly rust ;)

  • @chudchadanstud
    @chudchadanstud 3 місяці тому +1

    not the same thing mate. There are millions of animals on earth are you gonna make an enum for all of them?
    You're missing the point of dynamic dispatch.

    • @dario.lencina
      @dario.lencina  3 місяці тому +2

      There are many valid cases for enums and also for dynamic dispatch turns out that there are valid use cases for both

  • @naninano8813
    @naninano8813 4 місяці тому +4

    i heard that humans, all humans, are born to think in OOP and then some kneecap themselves by going functional or abandoning inheritance for aggregation and i tend to agree. OOP is literally made for humans, it is modeled after intrinsic human reasoning patterns.

    • @dario.lencina
      @dario.lencina  4 місяці тому

      Interesting perspective! While OOP can certainly align with how we naturally think about categorizing the world, functional programming offers powerful ways to handle complexity through immutability and pure functions.
      Both paradigms have their strengths and are tools that can be used depending on the problem at hand. It's all about choosing the right tool for the job!

    • @Redstoner237Channel
      @Redstoner237Channel 4 місяці тому +3

      Sure, OOP does seem natural (a truck is a type of car, which is a type of vehicle, etc.), aggregation makes just as much sense. A truck, a car, and a vehicle all help to transport someone else. By organizing structures based on what values they share, it can become really easy to use code.
      For example, a bee and a bird can both fly, so it would be easy to just make a Flyable trait to describe the behavior for any flying animal. Inheritance makes it a bit tricky. How would a bird inherit from a bee? We could have a FlyableAnimal class, but bees and birds can also walk. Would they need to inherit from a WalkableAnimal class then? What if we want to include planes? A FlyableObject class? How would that relate to Flyable animals? With a Flyable trait, it would just be right there.
      Describing objects based on what they can do makes a lot more sense if you are more concerned about the question "what can this do?". I feel like OOP answers the question of "how are these related" which is suitable for some purposes, but a lot of times you will find yourself rewriting code because two things aren't related yet do similar things

    • @dario.lencina
      @dario.lencina  4 місяці тому

      @Redstoner237Channel this explanation is sick!

    • @justanothercomment416
      @justanothercomment416 3 місяці тому

      @@Redstoner237Channel This is one of the classical failures of the current education system. There is an entire discipline on modeling data. Called, you guessed it, "data modeling." People cry and moan about OOP as if OOP is the issue. In reality it's almost always a poor, or worse, a bad education, on modeling data and data structures.
      Part of this education centers on both normalization and denormalization. I can't begin to tell you how many times I've met coders who were never taught the most trivial rule of OOP; "is a" vs "has a." If you don't know what this means, you just confirmed you received a second rate education on OOP. Worse still, coders are commonly taught that composition is not only not part of OOP, but an alternative to it. Composition has always been an essential element in data modeling, which means OOP.
      If you're looking for a point to start, look at data modeling for relational databases. This will eventually lead you to ORM.
      OOP is rarely the problem. It's almost always an issue with a lacking education or incorrect analysis/decomposition of the problem domain.

  • @ShimoriUta77
    @ShimoriUta77 3 місяці тому +1

    And the problem is even worse if you do this in an async context.
    Box go brrrt

    • @dario.lencina
      @dario.lencina  3 місяці тому

      Hahaha those genetics can get pretty unhinged 😆😆😆😆

  • @theairaccumulator7144
    @theairaccumulator7144 4 місяці тому +2

    even c++ templates are easier to understand than rust

    • @aykxt
      @aykxt 4 місяці тому +3

      skill issue

    • @dario.lencina
      @dario.lencina  4 місяці тому +1

      @theairaccumulator7144 @aykxt said it ^^ you need more hours in the saddle

    • @antoniong4380
      @antoniong4380 4 місяці тому +1

      According to what I understand , C++ templates works by assuming you, the user of the template, is passing types that has the used methods or field names. In some sense is a sort of Compile-Time reflection. I. E.
      If the object you're passing has a ".cry()" method for a Car(enterain that a car can cry for a moment) , then you can pass it to the template even though at the time of you writing the template that ".cry()" was for animals/humans.
      Now for a more finer example, Writers.
      In a game, a human entity can write. However in low level there's also another kind of Writer, which in Rust are often aliased as Sinks, or just your stdout in many languages.
      # Key takeaway
      The point is, there's no guarantees for you, as a library author, to know what the libraries users would pass through the template, and there's also no guarantees that your random joe will know what is actually meant to be passed through the template without type hints like "This receives a Hashing function" or whatever.

    • @izagawd
      @izagawd Місяць тому

      @@antoniong4380that’s why in the latest c++ version you can make templates require things
      You can make it require that it inherits from class X, or make it require that it IS class X, and even make it require that if u do a + b, where a and b are of type T, that they return a specific type. And if those requirements are not met, the compiler will throw an error, and tell you something like “T does not inherit from class Animal”
      Also, you can store those requirements in “concepts”, so you don’t have to repeat code. Templates are REALLY powerful

    • @izagawd
      @izagawd Місяць тому

      @@antoniong4380you can make requirements in c++ templates, and it will throw a detailed compile error now. C++ added concepts in one of those versions. So I can be like T must be a Book, and if I put say pen as my template argument, it will tell me a detailed error “Pen does not meet the requirement: inherits from book”
      C++ has evolved as well. And I would argue that templates in c++ are more flexible, since u can be like
      T must either inherit from Book or Food

  • @renatoborgogni2534
    @renatoborgogni2534 4 місяці тому +1

    grazie