Rust Programming: TypeState Builder Pattern Explained

แชร์
ฝัง

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

  • @hojjat5000
    @hojjat5000 ปีที่แล้ว +51

    I appreciate the amount of work you put into your videos.

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

      Thanks, greatlly appreciated.

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

    This, and the previous video, are excellent - thank you. I'd love to see more of these design-focused videos from you, please. There are many Rust beginner/syntax videos, but a video on this kind of topic, where the strengths of the language are used to build useful software, is quite rare.

  • @ankar71
    @ankar71 ปีที่แล้ว +16

    So, in effect, you have some kind of Design by Contract in compile time. You can design an API in a way that it is impossible to have structures in an inconsistent state and the only checks that will remain in runtime will be those that are impossible to know at compile time like validation checks and runtime errors which in Rust you are forced to handle anyway because they are behind Option or Result.

    • @JeremyChone
      @JeremyChone  ปีที่แล้ว +8

      Yes, this is the gist of it. It comes at a cost but can be worthwhile in some scenarios.

  • @HaroldR
    @HaroldR ปีที่แล้ว +26

    Doesn't build() become infallible? You should be able to omit Result and just make it return T

    • @JeremyChone
      @JeremyChone  ปีที่แล้ว +17

      Oops, that is correct. I missed this one somehow. Thanks for the note. (I will add it as a description note).

  • @flyaruu
    @flyaruu ปีที่แล้ว +50

    Nice one, I'm slowly getting used to the enormous number of generic types in Rust. One downside I see is that the compile time errors aren't as clear as the runtime errors. "method 'build' not found in RequestBuilder" isn't the clearest way to communicate that the consumer hasn't called the url method. Do you know of ways to improve that?

    • @JeremyChone
      @JeremyChone  ปีที่แล้ว +16

      Good point. Unfortunately, beside naming your state as clearly as possible, I do not think there is a way around that.

    • @LimitedWard
      @LimitedWard ปีที่แล้ว +14

      I agree, it does definitely make it a bit harder to read the error messages. I think the best workaround would be to give descriptive names for your type state structs. Instead of "NoUrl" you could use "MissingUrl" or "WithoutUrl". Then the error message would read "method not found in `RequestBuilder`". The word "no" feels more neutral whereas "missing" or "without" imply something is not present when it should be.

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

    Awesome! Really powerful the way you use generics and you are allowed to compose them independently

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

    Exactly what I was thinking when i requested it. Appreciate the quick turnaround time!

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

      Thanks for the request. In a way, it is good it is in its own video. There is quite a bit of information.

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

    Haven't got much time lately but whenever I see your video Jeremy its always delight. Happy new year. I know your channel is going to explode this year.

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

      Thank you for this super nice comment. Trying to produce more without altering quality.

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

    Excellent video! Thanks. I have been making sincere attempt to build all applications/components by being _Type-driven_ ! I have been reasonably successful in Scala and Java, as well! I am trying to get used to similar direction in thinking about the _Types_ in Rust too. This video explains the approach very lucidly! 👍

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

    That video was awesome. Brilliantly depicted the mechanism. Easy to follow. Love it

  • @KayOScode
    @KayOScode 9 หลายเดือนก่อน +2

    Very interesting. It does make me wonder if this is something you’d even want to do since there’s a lot of boilerplate involved. Due to the number of datapoint wise moves, for bigger structures it could get annoying to add new fields. But it’s still really cool

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

      Yes, agree. This is not to be abused.

  • @jorgeosorio1613
    @jorgeosorio1613 ปีที่แล้ว +5

    Thank you for taking the time to make these videos, they are very helpful.

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

    Insightful and easily adaptable for my own projects, thanks!

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

    makes me motivated to keep learning rust as the compile time checks are powerful.

  • @FreshLlamanade
    @FreshLlamanade ปีที่แล้ว +5

    Wow, Rust makes this pattern really elegant to implement compared to other languages like Java! Great video, I subbed :)

  • @hojjat5000
    @hojjat5000 ปีที่แล้ว +5

    Very clear and very useful. Great video.

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

    Nice pattern but if some values are mandatory why not have them as parameters to the new function?

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

      You are correct. A constructor for the required props and returning a simpler builder for the optional ones is also a good approach. It depends on the interface that makes the most sense for the particular case. But starting simple is always a good first approach.

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

    Fantastic video, cleared all my confusion!

  • @rsalmei
    @rsalmei 7 หลายเดือนก่อน +1

    This is the best typestate video I've seen, thank you!

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

    yessss this is MUCH better than the builder pattern!! thank you for this

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

    Thanks, this was tremendously helpful and made it very easy to recontextualize to my own project. Really enjoy this channel for intermediate Rust concepts!

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

    Thank you Jeremy, this was a great video and I'm glad youtube put this in my feed.

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

    It is time to do new video brother! You are my favourite! :D

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

      Yes, working on a big one as we speak! It will probably be next Sunday. Full intro on Axum.
      Also, I made some arrangements to focus more on those videos and awesomeapp.org for the rest of the year and more if it works well.

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

      @@JeremyChone wow, I'm excited!

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

    Very nicely explained ! Thank you!

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

    Excellent example and explanation! Thanks!

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

    Thank you so much for the great content. You are an awesome teacher.
    Greetings from Iran :)

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

    Really great example. I can imagine JWT might be a situation where you don't want builder methods called after it is "sealed". How does #[must_use] fit into this pattern? Can you do a real world video on #[must_use] ?

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

      Thanks.
      `#[must_use]` is a nightly only, right?

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

      ​@@JeremyChone `#[must_use]` as an attribute on types, traits, and functions is stable. `std::hint::must_use(...)` is unstable.

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

    I hope there's a crate that does all this work without taking up space in my own code (maybe using macros?). I love the ergonomics of using the builder struct, but I'd dread having to go back into the builder code and add or remove a field in 6 months.

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

      Not that I know of. Also, not sure a crate will add much value.
      These methods need to be implemented within the right scope (i.e., types/generics), so I am not sure external macros or types will help make the code more readable and easier to maintain. It might have the opposite effect.

  • @crazyingenieur3277
    @crazyingenieur3277 7 หลายเดือนก่อน +1

    I am simply amazed!!!

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

    I implemented this pattern several years ago for some generated code and only recently found out what it's called. It was in C++ so I used a bitmask for mandatory fields

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

    Very well explained, but a very confusing topic! I'm curious to see peoples questions.

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

    You are amazing, How can we support you?

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

      Thank you for the encouragement. For now, just sharing those videos will help a lot.
      I typically like to do the Rust subreddit sharing myself (usually a week later), but if viewers can share the video(s) on other social and coding platforms, that is always welcome. No pressure though, share the ones you really like.
      Thanks for asking and happy coding!

  • @cheebadigga4092
    @cheebadigga4092 10 หลายเดือนก่อน +1

    Awesome!! Thank you!

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

    Really amazing video. Thank you

  • @BrandonDyer64
    @BrandonDyer64 4 หลายเดือนก่อน +2

    I believe you can use the unit type `()` instead of `NoUrl` and `NoMethod`. So have ::new() return a `RequestBuilder`.

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

      Good point. I did not test it, but I think it should work and could be a valid approach.

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

    What's the benefit of this setup over just not deriving Default and setting any default values you might want in the new function? The new function must be supplied with a url, and if someone happens to instantiate the struct directly, they must provide a value for url anyway?

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

      Ah, that can be a strategy. And the "Default" here is not part of the pattern.
      The point of this pattern is that these are technically different types, so the first type won't even have the other functions, making it compile/type proof.
      However, this should not be abused, as it can get quite complicated at times.
      So, it's a good pattern to know, but not necessarily one to use often.

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

    Does the size of the types have any impact on binary size or is the overhead mostly related to the amount of types?

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

      Good question. Assuming the code uses all of those variants, it will impact binary size. The compiler "monomorphize" the generics (create another type/function for the used generic type)..
      Same when you use "impl Into" in a function argument. The compiler will create a duplicate function for each used concrete type.

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

    Very confused. I figured it out two weeks later.it turned to be function programming style, if u think of it this way, it's super easy to understand it.

  • @cunningham.s_law
    @cunningham.s_law 4 หลายเดือนก่อน

    how do you feel about the derive_builder crate?

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

      I am not a big fan (yet) of those derive builder libraries.
      They can remove some boilerplate, but they enforce practices that are sometimes unnecessary. I find myself spending more time figuring out how to opt-in/out of those features rather than just coding in the style I want.
      For example, one of those derive macros has the .build() returning a Result, but sometimes I don't need that; I want them to be infallible.
      Additionally, in some cases, like in my new GenAI library, I have `with_...` setters in the builder style, but I don't need a .build() as those can just work on the target type.
      Typically, for my APIs, I like to design sometimes with_... (setter builder style), from_... (constructors), new(..) (default constructors with the most common data), and default() for infallible default constructors.
      Not worrying about learning those macro library notations and just focusing on the style I want helps me prioritize user ergonomics. Later, I might remove some of the boilerplate behind the scenes, but that is not the priority.

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

    Amazing channel

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

    Outstanding!

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

    I think this is a good idea.... here is a different video showing the same idea th-cam.com/video/bnnacleqg6k/w-d-xo.html

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

    Excellent video, thank you!