State Design Pattern in Rust

แชร์
ฝัง
  • เผยแพร่เมื่อ 25 ธ.ค. 2024

ความคิดเห็น • 80

  • @letsgetrusty
    @letsgetrusty  3 ปีที่แล้ว +2

    📝 Get your *FREE Rust cheat sheet* : www.letsgetrusty.com/cheatsheet

  • @willharris745
    @willharris745 2 ปีที่แล้ว +22

    9:04 To avoid the unnecessary wrapping in `Option`, you could use `mem::replace` instead of `Option.take`, which gets rid of the need to have the intermediate None value.

  • @ErikBongers
    @ErikBongers 3 ปีที่แล้ว +13

    To those wondering why not use a simple enum, yes, in this simple case, the whole setup is a bit overkill. But imagine a complex machine with lots of sensors, motors, etc...in such a case it would be more maintainable if you have a 'self-aware' state that 'knows' what it can and cannot do, rather than have lots of nested "if(state=something) then/else..." spread all over your code. E.g. if you have a Sleep state, a programmer would find it much easier to add extra restrictions to what tasks can still happen in the Sleep state if all that stuff is grouped in one SleepState definition.

  • @gusslx
    @gusslx 7 หลายเดือนก่อน +3

    For those saying "just use an enum", every time you use the post you'd have to check (match) the state before using its methods.
    The type-enforced state is much better because Rust is aware of an instance type, so the state is already known at every point in time.

  • @TheRealFallenDemon
    @TheRealFallenDemon 3 ปีที่แล้ว +45

    How is it I can trawl through docs for days trying to understand the ' annotations and still be lost, but you just brush over it in 20 seconds and I suddenly get it? Awesome job man, thanks for that

    • @demyk214
      @demyk214 ปีที่แล้ว +1

      It’s called experience

  • @ChumX100
    @ChumX100 3 ปีที่แล้ว +23

    I feel this chapter was sort of a weird flex from the book authors: "Look, we can implement OOP patterns in Rust if we really wanted to. Although our language features make it kinda ugly. But those same features allow us to make much nicer implementations that take advantage of compile time error safeguards."

    • @Ragnarok540
      @Ragnarok540 2 ปีที่แล้ว +7

      I guess is a way to show that taking advantage of the features of the language is better than trying to force the use of a OOP pattern, which could be considered over engineering the problem.

  • @phenanrithe
    @phenanrithe 2 ปีที่แล้ว +7

    That's one way to do it but I find it heavy. I'd rather use the interesting null cost of an empty struct marker type pattern, and define 'struct Post', empty structs for 'Draft', 'PendingReview' and so on. Each of those struct with the methods in their implementation, instead of fake ones like here. This requires to reassign 'post', but is much lighter and better isolates the specific methods. I suppose it depends if there are many common methods to all the states, which may take more code space when defined in the generic implementation 'impl Post { ... }'.

    • @赤金蓮
      @赤金蓮 2 หลายเดือนก่อน

      Thank you for this comment. I would not have thought to do this and it seems like a fantastic approach for a number of my projects

  • @nirmalyasengupta6883
    @nirmalyasengupta6883 ปีที่แล้ว

    Excellent stuff. Thank you. I had had the opportunity to put to use principles of FSM in designing software for Credit-Card Auth Protocol, CDR processing in Telelcom, Online Multiplayer Gaming, Contests in Training by Gamification etc., using 'C', 'Java' and 'Scala'. I am learning Rust and this presentation guides me towards building similar software in Rust.
    Letting Types do that talking, in a manner of speaking, is not straightforward in my experience but once done, its adoption benefits are manifold.

  • @bluejazz42
    @bluejazz42 3 ปีที่แล้ว +73

    Why not just use a State variable of type enum?
    This would make it possible to create a much simpler and vastly clearer program. The mapping between the problem and solution should be as direct and clear as possible. The structure of the solution should mimic that of the problem.
    Its also easier to start with a state diagram, defining each state with arcs between them representing each valid state transition. Great for team and user discussions.
    Make it as simple as possible, but useful

    • @Roms8313
      @Roms8313 3 ปีที่แล้ว +6

      correct me if I'm wrong but I think the downside in this example of using an enum is that you can't implement a trait for an enum variant specifically, so you would have to implement methods differently, probably with a Result type as output

    • @bluejazz42
      @bluejazz42 3 ปีที่แล้ว +4

      @@Roms8313 Yes I agree. I think the object oriented version makes it easier to dynamically alter the ‘states’ by adding or removing ‘state objects’ . Whereas if an enum is used to define the set of states it can become more static in nature. But of course there may be mechanisms to extend set of enumeration values 🙂, which would have the benefit of making the evolution of the program more evident. My preference just happens to encourage the simplicity and visual clarity of the program as a whole throughout its lifetime of use and adaption to user and platform needs as they evolve. I always try to have compassion for the unfortunate maintainer who some time in the future has to review the implementation, understand it and its implications (performance, limits, testability …) and the add in support for correcting implementation errors and/or new features.

    • @scheimong
      @scheimong 3 ปีที่แล้ว +12

      I was about to make this comment. The approach Bogdan used is very OOP-esque and not particularly idiomatic in my opinion.
      Personally I would much prefer to use a non-exhaustive enum wrapped in a Post struct in this particular case, and put all the business logic within methods of Post.
      This has three main advantages:
      1. Less code duplication and boilerplate code
      2. Static dispatch, therefore more efficient
      3. The caller is better able to get feedback on the status of each transaction, if those methods return a Result type. (Although I suppose that's possible too in Bogdan's approach)

    • @skyeplus
      @skyeplus 3 ปีที่แล้ว +3

      I agree. I would have gone for enum too.

    • @ChumX100
      @ChumX100 3 ปีที่แล้ว +12

      I personally think that the state pattern is pretty ugly, specially if we need to deal with Rust details using things like Options and Boxes, but it does provide some maintainability advantages. Imagine we would add more states.
      With the enum approach, we would have a bunch of match expressions in all of our methods, and so we would need to go through each and every one of these methods and add arms correponding to the new states.
      Using the state pattern approach instead, we would only need to implement methods in the new states and possibly modify some other states.

  • @kirk7880
    @kirk7880 2 ปีที่แล้ว +1

    Beautifully explained! Thanks for taking the time to put this together. I’m really enjoying the content.

  • @bjugdbjk
    @bjugdbjk 2 ปีที่แล้ว

    Freaking amazing !! You have comeup with a very simple example to explain a complex apttern, Need more like this rusty !!

  • @hakanakdag9491
    @hakanakdag9491 ปีที่แล้ว

    wow I am new to rust and I see still I need to learn a lot of concepts. Thanks for this video.

  • @scott3489
    @scott3489 3 ปีที่แล้ว +1

    Yay, another video!

  • @saadabbasi2063
    @saadabbasi2063 3 ปีที่แล้ว

    Hey Man. I can’t do much for you than thanking you so much. ❤️
    Keep it coming ⭐️

  • @historichacker2013
    @historichacker2013 3 ปีที่แล้ว +1

    love your videos and rust :)

  • @hamzasajid7973
    @hamzasajid7973 3 ปีที่แล้ว

    You are a gem

  • @9SMTM6
    @9SMTM6 3 ปีที่แล้ว +3

    Hmm. I've really been trying to make the similarish builder-pattern work, but I have to admit I never found a situation where I did not eventually conclude that other abstractions proved superior.
    It's just that you end up with a bunch of different initialization states that make predictable behavior difficult, and you end up with a lot of methods that are meaningless after being called the first time.
    Edit:
    Yeah, the encoded in Type variant is more like what I've concluded is to be done if I really need it.

    • @Nuclear868
      @Nuclear868 3 ปีที่แล้ว

      Doesnt' it result in a new copy of all members, every time when the state is changed? As each state is different type, holding all needed members.

    • @9SMTM6
      @9SMTM6 3 ปีที่แล้ว

      @@Nuclear868 Well, probably yeah. The other pattern might just do the same though, and also thb copying is usually cheap compared to heap allocations. And the way at least Rusty implemented it the original pattern causes a lot of allocations, where the updated one causes none at all, because it doesn't use Box. At least IIRC.

    • @9SMTM6
      @9SMTM6 3 ปีที่แล้ว

      @@Nuclear868 Well, revisiting it, what I said is the case.
      Excluding string allocations that happen in any case.
      Although, also, I'm not sure how Rust treats these zero size struct in boxes, it might optimize them away.
      TBH I would not think about optimizations when any of these patterns are regarded. They're made to be easy to use, not to perform well, and their usage does not tend to end up in hot loops, if they do you should rethink.

  • @ahmedhafiz2419
    @ahmedhafiz2419 3 ปีที่แล้ว

    Wow. Much needed video. I have been trying to learn these patterns for a while. This video explains state pattern very nicely and I understood more than I did by reading other materials. This video also shows rust having better approaches, which blew Mind blown.
    Thanks a lot.
    What do I search for to learn more on such 'rust patterns'?

  • @netify6582
    @netify6582 2 ปีที่แล้ว +4

    The second approach switching types is looks pretty robust. For code where I want really strict safety type checks during compile time, this might be way to go.

  • @learntry9074
    @learntry9074 2 ปีที่แล้ว

    this one was over the head , may be I need to revisit it again in some days..

  • @nicohussein2635
    @nicohussein2635 ปีที่แล้ว

    Hey! How would you use macros to avoid repetitive code (request_review and approve functions) in this example?

  • @alexandersemionov5790
    @alexandersemionov5790 2 ปีที่แล้ว

    I was able to replicate code and make a ASCII animation with it. Thanks for the video really helpful stuff.

  • @caiofernandes3170
    @caiofernandes3170 3 ปีที่แล้ว +4

    Hey, man.
    You know what's going to be interesting... Talk about data-oriented in RUST.
    I'm doing research on this topic but is not easy to have examples...

    • @ilyasb4792
      @ilyasb4792 3 ปีที่แล้ว +4

      Try checking for some ECS, it's a key concept of data oriented programming, although mainly used in game programming. The idea is to reduce cache misses by keeping every component in it's own list, then making systems that are basically some queries to get logic done. An entity is just a number, so it can be copied easily.
      If it's too hard to start with, try beginning by understanding what are generational arenas allocators. You can check Hecs for an ECS in rust. And there is some great talks by professionals on the fields about this.
      Hope it helps :)

    • @caiofernandes3170
      @caiofernandes3170 3 ปีที่แล้ว

      @@ilyasb4792 That was aweasome man, thkx!

  • @phenanrithe
    @phenanrithe 2 ปีที่แล้ว

    By the way, why do we need to provide our email for the cheat sheet, what are you doing with that information? "I have been fully informed and consent to the collection and use of my personal data **for any purpose in connection with the software, products and/or services**." seems pretty wide.

  • @Tferdz
    @Tferdz 3 ปีที่แล้ว +2

    4:29 you could have used Self instead of Post there.

  • @oluwatomisinbabatunde8426
    @oluwatomisinbabatunde8426 3 ปีที่แล้ว

    NGL you’re very good and consistent do you write c or c++

  • @googleuser2016
    @googleuser2016 3 ปีที่แล้ว +1

    Hi nice Video again! Why not invest in some light setup? There are cheap sets that make a lot difference.

  • @magnoelmagnifico957
    @magnoelmagnifico957 3 ปีที่แล้ว +13

    I don't get this. Why don't you just use an enum?

    • @Bolidoo
      @Bolidoo 10 หลายเดือนก่อน +5

      As you could see in the video, there are many ways to get the same outcome. The state design pattern provided encapsulation, that you would lose if you just used an enum. Meaning, if every function has a huge switch statement, all the logic of every state is inside that function, instead of being distributed in each state. If states are complex, separating/encapsulating things may be desirable. Also, the second aproach is also another way of solving the same situation, using the type system. There are always many ways to do the same, so ideally you should know a bunch and waver their pros and cons to use the one that better suits your needs.

  • @rafaelbachmann7246
    @rafaelbachmann7246 3 ปีที่แล้ว +2

    Type state pattern would be ideal here I think

  • @trunghieule8982
    @trunghieule8982 2 ปีที่แล้ว

    Why do we use "state: Option"? why cannot we use "state: Option" ?

  • @aloussase
    @aloussase 2 ปีที่แล้ว

    great vid! That example is similar to the one on The Book, did you take it from there? Also I like your font, what's its name?

    • @nofacee94
      @nofacee94 2 ปีที่แล้ว +1

      This is taken directly from The Book and he has a series of videos going through each chapter: th-cam.com/play/PLai5B987bZ9CoVR-QEIN9foz4QCJ0H2Y8.html

  • @tech3425
    @tech3425 3 หลายเดือนก่อน

    that is cool!

  • @jonathanmoore5619
    @jonathanmoore5619 3 ปีที่แล้ว

    always a gem.,

  • @daque1960
    @daque1960 2 ปีที่แล้ว

    (To anyone) Would there be any advantage to put all the state types into the fields an enem ? You could use match on the variant to choose which method to run and all enum variants would have to be covered or you would get an error.

  • @SamuelIbarraSolis
    @SamuelIbarraSolis 3 ปีที่แล้ว

    what is your font name?

  • @herrxerex8484
    @herrxerex8484 3 ปีที่แล้ว +1

    Ok this is a hard to understand , but gonna push through it

  • @HyperManu
    @HyperManu 3 ปีที่แล้ว +1

    insta subscribe :) Keep the good content coming!

  • @kuqmua755
    @kuqmua755 3 ปีที่แล้ว +2

    Why we need to implement function if it has no affect? Just return self... Why just don't implement it at all?

    • @ChumX100
      @ChumX100 3 ปีที่แล้ว +1

      Because we are relying on each state to have that method, even if it does nothing, since we are calling those methods independently of the state the post is in.

  • @Nichiyoobiko
    @Nichiyoobiko 2 ปีที่แล้ว

    In the OO approach you have a circular dependency between Post and State. Is it possible to implement State without any knowledge of Post?

  • @ИнякинАлександр
    @ИнякинАлександр 3 ปีที่แล้ว +2

    Use an Enum, Bogdan!

  • @dabzilla05
    @dabzilla05 3 ปีที่แล้ว +4

    As always, GREAT content. Really gave allot of great concepts that I have been working with myself during my development of my task manager.
    A few notes, while I know you end up changing the implementation in the second part, the state field gave me major "maybe not?" vibes: th-cam.com/video/YR5WdGrpoug/w-d-xo.html. as a general rule, I try to avoid maybe types like Option within the actual struct body (see video for reasons why).
    Your second example was probably more idiomatic, but I think a small change you could make is giving a reference to self, rather than having the function consume self (or even the mutable reference for that matter) and simply return a NEW variant of the object with the required changes.
    This ends up being MUCH less finickey with ownership since you no longer need to think about the ownership change flow when putting this into a larger program, these methods can be chained or done atomically as one could see fit. And while the idea of the old version being consumed seems more elegant, there are often many good reasons to keep the former version of the object around just in case you need to use it some more (a great example is a comparison function where you tell the user that you converted an object from one state to another or something). Finally, if we end up not needing the old object after we get the new one, it will simply be cleaned up at the end of the function due to rust's dropping mechanism.
    While a die-hard performance buff would argue that creating a new object is inefficient, I have found that there is no real performance loss in day-to-day use unless you are REALLY resource constrained, and in exchange you get a functional layout that makes reasoning about an object's ownership status at any time trivially easy (even within an async workflow since there are almost no locks from an object holding a mutable reference hostage from the other threads).
    This was a GREAT showcase of how the object design pattern is implemented in rust and i'm excited for more content.

  • @iyxan23
    @iyxan23 3 ปีที่แล้ว +2

    hey is there any chance of you publishing these explaining videos' source code into github or something? I wanted to look deeper and play around with the code myself
    anyway, great videos! I hope your channel blow up very soon :)

    • @letsgetrusty
      @letsgetrusty  3 ปีที่แล้ว +1

      You can get the code from the Rust Book: doc.rust-lang.org/stable/book/ch17-03-oo-design-patterns.html

  • @GolangDojo
    @GolangDojo 3 ปีที่แล้ว

    Big D energy

  • @shravanshetty347
    @shravanshetty347 3 ปีที่แล้ว +2

    Request for unsafe!

  • @davidvernon3119
    @davidvernon3119 2 ปีที่แล้ว +2

    What problem does this approach solve? This seems overly complicated for what it is doing.

  • @vorrnth8734
    @vorrnth8734 2 ปีที่แล้ว

    Is it not possible to construct all these structs without calling new methods? That way we can bypass the whole state system an e.g. construct a Post right at the spot.

  • @syntaxed2
    @syntaxed2 3 ปีที่แล้ว +1

    Wut, I have never heard of a State Design Pattern...but then again I am a noob :D

  • @umut8244
    @umut8244 3 ปีที่แล้ว +1

    Functional approach in Rust is much better.

  • @emvdl
    @emvdl 3 ปีที่แล้ว

    молодець

  • @DaminGamerMC
    @DaminGamerMC 2 ปีที่แล้ว

    all really cool but why not just use enums for the state... Why is the pattern even needed in the beggining

  • @loarca11
    @loarca11 10 หลายเดือนก่อน

    Honestly, that specific case could be better by just using enums

  • @bbkr2063
    @bbkr2063 7 หลายเดือนก่อน

    First example with unified interface quickly backfires - returning empty string as "special value" instead of Option is sketchy AF. The only advantage of this approach I see is that Box logic is hidden from library user. While there may be situation in second solution where user would have to use Box to encapsulate all different state types himself.
    BTW: I love this taking ownership of self trick. It makes second solution so bomb-proof just by pure language design. You cannot accidentally "fork" states and keep using old one and new one at the same time.

  • @dragonmax2000
    @dragonmax2000 3 ปีที่แล้ว +1

    maybe slow down a bit and explain more?

  • @xExekut3x
    @xExekut3x หลายเดือนก่อน

    ngl, you always look high af