Crust of Rust: Dispatch and Fat Pointers

Поділитися
Вставка
  • Опубліковано 28 лип 2024
  • In this episode of Crust of Rust, we go over static and dynamic dispatch in Rust by diving deep into generics, monomorphization, and trait objects. As part of that, we also discuss what exactly the Sized trait is, what it's for, and how it interacts with Dynamically Sized traits.
    0:00:00 Introduction
    0:03:08 Monomorphization
    0:16:13 (Static) Dispatch
    0:22:49 Trait Objects
    0:27:13 The Sized Trait
    0:39:34 Sizing Unsized Types
    0:46:47 Can I Recover The Concrete Type?
    0:47:56 Dynamic Dispatch
    0:53:08 Vtables
    1:02:13 Limitation: Multiple Traits
    1:08:32 Limitation: Associated Types
    1:10:30 Limitation: Static Trait Methods
    1:15:48 Disallowing Trait Objects
    1:20:58 Limitation: Generic Methods
    1:30:53 Limitation: No Non-Receiver Self
    1:33:00 Partial Object Safety
    1:39:54 Dropping Trait Objects
    1:43:03 Dynamically Sized Types
    1:48:30 Manual Vtables in std
    1:51:45 Q&A: Making Your Own DST
    1:53:41 Box([u8]) vs. Vec(u8)
    1:55:18 dyn Fn() vs. fn() vs. impl Fn()
    2:02:00 No Coherence This Stream
    2:03:00 Runtime Trait Detection
    2:04:41 Double-Dereferencing dyn Fn()
    2:06:55 Unsafe Vtable Comparions
    2:09:06 Slice of Trait Objects
    2:10:05 Codegen Units and Vtables
    2:10:55 The Any Trait
    You can read more about Rust monomorphization in the Rust book at doc.rust-lang.org/book/ch10-0.... It also has some good discussion about Trait Objects (doc.rust-lang.org/book/ch17-0...) and Dynamically Sized Types (doc.rust-lang.org/book/ch19-0.... For more details, the Rust reference also has more details on Trait Objects (doc.rust-lang.org/nightly/ref...) and Object Safety (doc.rust-lang.org/nightly/ref..., and so does the Rustonomicon (doc.rust-lang.org/nomicon/exo....
    Live version with chat: • Crust of Rust: Dispatc...
  • Наука та технологія

КОМЕНТАРІ • 89

  • @10e999
    @10e999 3 роки тому +85

    I'm currently starting the Crust of Rust series from the start.
    Just here to thank you for your work.
    The topics are interesting and your examples /explanations well crafted.

  • @TheKaratekidd32v
    @TheKaratekidd32v 3 роки тому +26

    Thanks for adding timestamps to these videos! It makes it easy to watch a couple 'chunks', and come back to the video later.

  • @omelettttttteeeeeee
    @omelettttttteeeeeee 3 роки тому +39

    This is some of the highest quality rust content I’ve seen. Thanks for your work and knowledge-sharing!

  • @chrisboyce5009
    @chrisboyce5009 3 роки тому +14

    These videos are great! For me, the first third is usually "I totally understand what's going on", next third is "I understand some of these words", and make it another 10-15 minutes before starting the video over. The Q&A is really helpful too, since someone in the chat always asks about a specific corner case or exception. It doesn't seem like there is a lot of good intermediate-level material out there, so I'm always excited to see a new video.

  • @revanthshalon5626
    @revanthshalon5626 26 днів тому

    I came here after reading the book Rust for Rustaceans and this helped me understand a lot better. Thanks a lot Jon!

  • @Loige
    @Loige 3 роки тому +6

    Excellent video as usual! Thanks, Jon. I feel like dynamic dispatch and its constraints make a lot more sense to me now!

  • @touisbetterthanpi
    @touisbetterthanpi 3 роки тому +3

    You don’t even know how helpful this particular episode has been for me. And the detailed chapter markers too have been so nice when I need to refer back. Seriously. Thank you

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

    thank you so so much for these streams! I'm very interested in rust internals and I often come to your streams for clarification of complex stuff. really much appreciated

  • @nathanielwoodbury2692
    @nathanielwoodbury2692 3 роки тому +1

    Wow. This video was so incredible helping me understand rust deeper. Getting to understand on a more complex level and pushing my knowledge. Thank you so much for this video. I am so grateful for this.

  • @ihasmax
    @ihasmax 2 роки тому +1

    I really appreciate that you touch on very advanced topics while teaching beginner/intermediate ones. I've learned the most from your videos so far and have a deeper understanding of how Rust actually works

  • @Krzysztow1985
    @Krzysztow1985 3 роки тому +2

    Man, I wish I had your skill of explaining things. You're awesome! Of course, the amount of knowledge you have is helping here, but it's a skill on its own.

  • @pcfreak1992
    @pcfreak1992 3 роки тому +8

    I love these streams! Please keep them up!

  • @KaranKumar-wb5bn
    @KaranKumar-wb5bn Рік тому

    32:55 this has to be the best explanation.
    I literally had this doubt and I was about to google how a function would know the size of normal array otherwise. But you explained it so well right after that. Thank you so much for these videos.

  • @SaintlySpirit
    @SaintlySpirit 3 роки тому +7

    For those, who prefer visualizations over the long documentation text, visit cheats.rs. For example, the idea of fat pointers can be grasped from the cheats.rs/#references-pointers-ui section. Of course, this site is pretty much of any value if you already know Rust to some extent, but if you do, those little condensed explanations and visuals might be beneficial to the understanding of the whole picture!
    Thanks for these series, Jon! You should consider doing more visual explanations, whether by hand-drawings (as you rarely do on live coding streams) or by using cheats.rs!

  • @Zizaco
    @Zizaco 2 роки тому

    Great video. You gracefully explained what's behind the dyn keyword

  • @anssietelaniemi3397
    @anssietelaniemi3397 3 роки тому +2

    Really Great content. Opening up the internals explaining how things works under the hood. The missing pieces. And why I am hitting the head in the wall (compiler).

  • @marcorossetti4484
    @marcorossetti4484 3 роки тому

    Thank you so much Jon! Very helpful!

  • @willemvanderveen7567
    @willemvanderveen7567 3 роки тому

    Thank you so much, this is such good content man much appreciated.

  • @joaodiasconde
    @joaodiasconde 3 роки тому

    Great content. Like always!

  • @Codeaholic1
    @Codeaholic1 3 роки тому +4

    I love your intro animation.

  • @RiwenX
    @RiwenX 2 роки тому

    This was super informative, cheers!

  • @qm3ster
    @qm3ster 3 роки тому +2

    I feel like at 1:05:26 you should have also implemented HeiAsRef:
    pub trait HeiAsRef: Hei + AsRef {}
    impl HeiAsRef for T {}

    • @jonhoo
      @jonhoo  3 роки тому +3

      Yeah, in practice you'd want that blanket implementation too, but it wasn't really relevant to the discussion around vtables :)

  • @antoniocorbibellot6532
    @antoniocorbibellot6532 3 роки тому

    Thanks Jon for this enlightening video! Pure Gold.
    In case someone wants to see in action handcrafted vtables in C-land he/she can have a look at the headers from the Gtk toolkit or even have a look at the C-code output that the vala language compiler produces.

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

      This video and ua-cam.com/video/1-_EBEr0fxI/v-deo.html which goes over GObject have been very handy for a project of my own.

  • @KohuGaly
    @KohuGaly 3 роки тому +7

    I wish you have covered the Any trait. It seems like it's super relevant to this topic.

    • @jonhoo
      @jonhoo  3 роки тому +2

      There really isn't much magic to Any. It's just a method that returns a unique type identifier that then allows safe downcasting as I mention at the end :)

    • @KohuGaly
      @KohuGaly 3 роки тому +3

      @@jonhoo I feel like "Can I downcast trait object back to original type?" is a fairly important question when discussing dynamic dispatch. And "Any" trait seems to be the idiomatic way to do it.

    • @jonhoo
      @jonhoo  3 роки тому +3

      @@KohuGaly Ah, so, the answer is that you can only do it *if* your trait object includes the Any trait. You can't use Any to downcast an arbitrary trait object.

    • @KohuGaly
      @KohuGaly 3 роки тому +1

      @@jonhoo So basically, the Any trait is optional... as per "don't pay for what you don't use" principle...

    • @jonhoo
      @jonhoo  3 роки тому +2

      @@KohuGaly Yeah, you can think of it that way!

  • @amaraojiji
    @amaraojiji 3 роки тому

    Thank you a lot! I love those!

  • @CyborusYT
    @CyborusYT 2 роки тому

    3:04 "Let's get rid of this test, we don't need no test" Famous last words!

  • @samighasemi3333
    @samighasemi3333 3 роки тому

    Thank you for this video

  • @enbytiousmusic
    @enbytiousmusic 2 роки тому

    Thank you! Very good Video! Keep it up! Greetings from Germany 👋

  • @mithradates
    @mithradates 3 роки тому

    One day implementing that compiler error at 1:06:05 sounds awesome!

  • @user-vr7dv8yf9y
    @user-vr7dv8yf9y 3 роки тому

    good tutorial, respect

  • @johnnyegel
    @johnnyegel 3 роки тому

    I just have to comment that this is a brilliant video if you are interested in Rust on a slightly deeper level. Kanonbra! :-D

  • @kiffeeify
    @kiffeeify 2 роки тому

    @1:05:40"HeiAsRef". I wonder who that Ref guy is and why he is so hei ;-)
    Awesome video!

  • @alliu6757
    @alliu6757 3 роки тому +1

    Thank you for your videos. Could you do a video about epoll and io_uring for file I/O?

  • @thepuzzlemaker2159
    @thepuzzlemaker2159 3 роки тому

    Thanks!

  • @sodiboo
    @sodiboo 2 роки тому +2

    15:33 Technically not entirely true. "Dynamic Libraries" is a broad term, and if you include for example, Windows' Dynamic Link Libraries, those can also contain .NET managed code. Those contain generic functions and types, and it is up to the runtime to "monomorphize" them just in time. But yeah, this is likely not what that question meant, since the context is dynamically linked *native* code

  • @TheZethera
    @TheZethera 3 роки тому +7

    Thicc pointer

  • @insomniaccoder806
    @insomniaccoder806 2 роки тому

    please please.. sir , if u can cover unsafe rust , it would be a great help for the community , but after all iam so glad u prepare this content for us ! tq so much 😃

  • @timanderson5717
    @timanderson5717 3 роки тому

    can't the generic method thing be solved by more dynamic dispatch? I.e. The vtable for Extend has extend?

  • @-sbin
    @-sbin Рік тому

    I'm reading your book and decided to look up a video on this concept to understand more... and guess who I find.

  • @hniksic
    @hniksic 3 роки тому +4

    At 1:41:05, should the drop function to accept `v: Box`? In the code as written, `v` is just a reference, so the compiler can't run `Drop::drop()` when it goes out of scope, since its owner might still be using it.

    • @xrafter
      @xrafter 2 роки тому

      Same question

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz Рік тому +2

    Isn't a vtable generated for each _type_ , not instance? What is the problem then in inserting a static method inside the vtable? Every other method takes atlest one parameter, but I don't see how not taking a parameter is problematic.

  • @michaelnajera7958
    @michaelnajera7958 3 роки тому +3

    Making me a better swift programmer, thanks. But now you got me curious, how does swift handle dynamic linking of generics?

    • @jonhoo
      @jonhoo  3 роки тому +4

      gankra.github.io/blah/swift-abi/ has all the gory details!

    • @michaelnajera7958
      @michaelnajera7958 3 роки тому

      @@jonhoo Thanks!

  • @VivekYadav-ds8oz
    @VivekYadav-ds8oz Рік тому +1

    Also you said that we can only include "+ " in our variable signature if some_trait is an auto-trait, because auto-traits have no functions. But isn't Any also an auto-trait? It surely has functions, and which have different implementations for each concrete type. Now I'm not really sure how that comes into play.

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

      No, Any isn't an auto-trait. It has a blanket implementation (impl Any for T), but that's not the same. An auto-trait is one whose impls are generated by the compiler directly based on the structure of the type, and they never have any methods, which is not the case for Any.

  • @antonioquintero-felizzola5334
    @antonioquintero-felizzola5334 3 роки тому

    Hey Joh, what's the font you use in your terminal?

  • @kushagragupta7051
    @kushagragupta7051 3 роки тому

    at around 1:05:52 when you talked about the multiple trait vtable, you said to create a new trait that requires the needed traits and use that. It would give a bigger vtable but it would still be 1 pointer right? And it would lead to code duplication as either the compiler would copy the trait implementations to another location or it would copy the function labels and call the original implementation methods. Wouldn't that be inefficient? Is there a way to say that I do want to generate a 3 pointer wide or even wider (based on number of traits) argument? with each pointer pointing to one traits vtable?

    • @ekremdincel1505
      @ekremdincel1505 3 роки тому +2

      You can already do what you describe yourself by using custom vtables but trait objects don't work like that.
      And compiler only copies vtables, which usually has the size of (pointer_size * method_count). Basically it is a very very little overhead.

  • @tdwebste
    @tdwebste 3 роки тому

    Document describing your vim setup.
    Please :)

  • @AllTheFishAreDead
    @AllTheFishAreDead 3 роки тому

    Hey, great video - I have a noobie qn. Early on you discuss how in the trait object case you need the function argument to be sized so you can make a collection of them where each thing, the pointer type, is of the same size. However later on you say we can have a trait not add a static method to the vtable by requiring Self be sized, saying that the trait, dyn Hei, has no size. But didn't we pass a reference so that it was sized? Thanks!

    • @xrafter
      @xrafter 2 роки тому +1

      Reference is sized not the object

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

    I’ve been doing a bunch of minor projects in rust and I haven’t even ran into a use case where I’ve needed to use “dyn”.
    In other languages I’ve used the pattern a lot and maybe it’s just that I’ve worked on a different class of problems than I did in those languages.
    But I’ve found that I can often get away with using some different pattern in rust.
    There definitely are use cases where it’s needed, or at least very useful. But where I in some languages would make an interface and put a bunch of generic objects implementing that interface, often I find it more clear to define an enum with variants holding types.
    That limits how generic the code is. By quite a lot actually.
    It only allows the possible values I explicitly put in there. But quite often I don’t want to be generic over any object that happens to implement a trait. I just want to be very restrictively generic to exactly 2 or 3 different things that I implement myself.
    I’ll probably run into it more when I ramp up complexity a bit.
    That’s why I’m eating the video.
    But still. We don’t have to be maximally generic for everything we write, just generic enough to make writing the code convenient.

    • @KohuGaly
      @KohuGaly 10 місяців тому +1

      To paraphrase Dijkstra, "purpose of abstraction is not to be vague, but to be precise". A good support for abstraction lets you state precisely what you mean, without forcing you to be more vague or more specific than you want to be.
      Languages that have interfaces (traits) but no sum-types (enums) force you to over-generalize and therefore force vagueness. They push you to express closed sets of types (enums) as open sets of types (interfaces), even when you actually want a closed set (which is most of the time, actually, as you correctly point out).
      The main use case for traits and generics is when you want to provide new functionality to types that you have no control over (ie. when you write a library). Or you you have control over those types, but you want to keep it separated to reduce complexity.

  • @blablaqwertyful
    @blablaqwertyful 2 роки тому

    It's tempting to say that type erasure causes all trait-related problems. Is it required that much to generate an efficient code? Why can't we preserve types?

  • @TomasSandven
    @TomasSandven 2 роки тому

    Holy shit you’re Norwegian!? Your English is amazing.
    Hilsen fra Molde :)

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

    does anyone know what theme is he using? looks pretty neat
    edit: looks like gruvbox or something

  • @parthikpatel6108
    @parthikpatel6108 3 роки тому

    How do you get your navigation bar/tabs on the bottom? Is that a firefox extension?

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

    44:30 "you can keep using box after the stack frame of the caller has gone away", how is this situation possible? Won't the caller return only after this function has returned?

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

    1:29:25 I still don't understand why it's not possible to monomorphize the vtables. We compile the standard library at the same time as the rest of our code, so it should be possible to know the size of the vtable for a type+trait after monomorphization, even if it is large, and use that everywhere in our program. My vtables will be differently sized than yours, but that's just a different flavor of the same issue preventing us from having dynamic libraries.

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

      Listening to your explanation again, I think my mistake is that we don't actually compile the standard library at the same time, we compile one crate at a time and just have access to the source for other crates. So for a normal generic function, if we introduce a use that std doesn't have, it would get compiled along with our crate, not std

  • @johanngambolputty5351
    @johanngambolputty5351 9 місяців тому +1

    Missed opportunity for
    ```
    match vial_broken {
    true => Box::new(DeadCat::new()),
    false => Box::new(AliveCat::new())
    }
    ```
    but Schrodinger hai is good too ;)

  • @qm3ster
    @qm3ster 3 роки тому

    in `struct Foo {f: bool, t: [u8], x: bool}` why can't the offset of `x` be determined by `len-sizeof x`?
    (such a struct combined with a #[repr] would be good for some zero-copy network encoding stuff)

    • @jonhoo
      @jonhoo  3 роки тому +2

      In general the compiler expects field offset to be constant. You could imagine extending it to be dynamic, but that's a big language feature in and of itself.

  • @jawad9757
    @jawad9757 2 роки тому

    What font do you use?

  • @OkamioftheRinnegan
    @OkamioftheRinnegan 3 роки тому

    Why does an unsized type have to be the last field in a struct if the compiler is allowed to reorder fields anyway?

    • @KohuGaly
      @KohuGaly 3 роки тому

      Because fields of struct are accessed as pointer offsets. For sized types, compiler may choose to reorder them, but compiler still knows where each field is located relative to the head of the struct.
      That does not work for unsized types. Because compiler can't know its size, then it can't access fields after it as offsets, because it can't figure out how big the offset should be. The only place where you (and the compiler) can put it is at the end of the struct.

  • @ryanleemartin7758
    @ryanleemartin7758 2 роки тому +1

    I have my own MIT professor as a Rust mentor for free. Well, don't we live in exciting times!

  • @ivanzvonimirhorvat9744
    @ivanzvonimirhorvat9744 3 роки тому +2

    IMHO It would be nice if you showed (traits) fat pointer representation with the debugger

  • @random6434
    @random6434 3 роки тому +2

    I disagree with the implementations at 7:34. I think they should still be `s.as_ref().len()` because you should be able to call it with any `T: AsRef`, even if T did not have a `.len()`. The optimizer might then see that for `&str`, `as_ref` does nothing and remove it. Having just `s.len()` makes it seem like there is something magical between `AsRef` and generics, which I don't think there is.

  • @elgusanito6991
    @elgusanito6991 3 роки тому +32

    Who is the guy who disliked the video?? Let's locate him and sacrifice his soul for the sake of nightly rust.

    • @xrafter
      @xrafter 2 роки тому +2

      @@john_says_hi
      .Its going out of hand , now there are six of them

  • @32zim32
    @32zim32 3 роки тому +1

    s::weird is no possible because s doesn not name a type
    As you said type erasure occurs and the only thing you have is pointer to data and pointer to methods table
    Compiler does not need to know s type to call needed method
    I think the main reason is because in machine code level calling method through indirection is compiled into some stuff, which requires pointer to data to be available at this point. That's the main goal of dynamic dispatch and polymorphism.
    Method without self parameter can only be invoked through concrete type, which make no sense to include it in vtable which is needed only for magic like calling method without knowing type of object this method is called on

  • @softwarelivre2389
    @softwarelivre2389 3 роки тому

    25:50 so we want something similar to Duck Typing in Typescript? Interesting

  • @mariuskriegerowski8378
    @mariuskriegerowski8378 8 місяців тому

    This is clearly an outlier: Usually I watch tutorials on youtube at playback speed = 1.5 (+- 0.25) because of relatively low information density. But crust of rust is often < 1.0. Thick (high quality) material to digest.

  • @jeffg4686
    @jeffg4686 3 роки тому

    Where is Norwegia?

    • @leontepe2329
      @leontepe2329 2 роки тому

      It's Norway. They speak Norwegian.

    • @jeffg4686
      @jeffg4686 2 роки тому

      @@leontepe2329 - yeah, i know. I always just think it sounds funny

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

    Thanks!