Rust Programming: TypeState Builder Pattern Explained

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

КОМЕНТАРІ • 66

  • @hojjat5000
    @hojjat5000 Рік тому +51

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

  • @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.

  • @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.

  • @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.

  • @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.

  • @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).

  • @agustindiaz3361
    @agustindiaz3361 Рік тому +12

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

  • @Hanneslueke
    @Hanneslueke 10 місяців тому

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

  • @FreshLlamanade
    @FreshLlamanade Рік тому +5

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

  • @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! 👍

  • @jorgeosorio1613
    @jorgeosorio1613 Рік тому +5

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

  • @KayOScode
    @KayOScode 10 місяців тому +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  10 місяців тому

      Yes, agree. This is not to be abused.

  • @rsalmei
    @rsalmei 8 місяців тому +1

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

  • @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.

  • @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.

  • @irlshrek
    @irlshrek Рік тому +2

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

  • @parthmittal5625
    @parthmittal5625 Рік тому +1

    Fantastic video, cleared all my confusion!

  • @shashanksharma21
    @shashanksharma21 Рік тому +1

    Very nicely explained ! Thank you!

  • @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!

  • @vsuryabhargava
    @vsuryabhargava Рік тому +1

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

  • @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!

  • @crazyingenieur3277
    @crazyingenieur3277 8 місяців тому +1

    I am simply amazed!!!

  • @BrandonDyer64
    @BrandonDyer64 5 місяців тому +2

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

    • @JeremyChone
      @JeremyChone  5 місяців тому

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

  • @12e2aela7
    @12e2aela7 10 місяців тому

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

  • @jocketf3083
    @jocketf3083 Рік тому

    Excellent example and explanation! Thanks!

  • @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 11 місяців тому +1

    Awesome!! Thank you!

  • @JeffHanke
    @JeffHanke Рік тому

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

  • @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.

  • @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.

  • @Turalcar
    @Turalcar 10 місяців тому

    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

  • @cunningham.s_law
    @cunningham.s_law 5 місяців тому

    how do you feel about the derive_builder crate?

    • @JeremyChone
      @JeremyChone  5 місяців тому

      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.

  • @obichinedufrank
    @obichinedufrank Рік тому

    Really amazing video. Thank you

  • @richardpayne
    @richardpayne 10 місяців тому

    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  10 місяців тому

      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.

  • @bobby9568
    @bobby9568 Рік тому

    Amazing channel

  • @alexandersemionov5790
    @alexandersemionov5790 Рік тому

    Outstanding!

  • @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.

  • @martinfrances107
    @martinfrances107 Рік тому

    I think this is a good idea.... here is a different video showing the same idea ua-cam.com/video/bnnacleqg6k/v-deo.html

  • @volking1
    @volking1 Рік тому +1

    Excellent video, thank you!