Rust Experiment Failed - Static Dispatch IS Hard | Prime Reacts

Поділитися
Вставка
  • Опубліковано 18 жов 2024
  • Recorded live on twitch, GET IN
    / theprimeagen
    Reviewed article: jmmv.dev/2023/...
    Written by: Julio Merino | jm...
    MY MAIN YT CHANNEL: Has well edited engineering videos
    / theprimeagen
    Discord
    / discord
    Have something for me to read or react to?: / theprimeagenreact
    Hey I am sponsored by Turso, an edge database. I think they are pretty neet. Give them a try for free and if you want you can get a decent amount off (the free tier is the best (better than planetscale or any other))
    turso.tech/dee...

КОМЕНТАРІ • 197

  • @Buzzec9101
    @Buzzec9101 Рік тому +161

    The issue with massive where clauses can be solved with traits and associated types. All the generics that are in the where clause on the struct can be put into a trait with all the required super traits. This makes you have only a single generic in your where clauses. DRYS

    • @krpp
      @krpp Рік тому +23

      Yeah, had a little WTF moment when he mentioned having like 34 copies of it across different files

    • @meanmole3212
      @meanmole3212 Рік тому +8

      Just came to say this, but it seems I don't have to. Traits can still be a mess, especially when mixed with lifetimes.

    • @ethgallucci
      @ethgallucci Рік тому +4

      had this exact same thought

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

      Yeah wanted to say this too

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

      There's a lot of places you can't do that yet, e.g. inherent associated types

  • @trashcan3958
    @trashcan3958 Рік тому +26

    I don't get the obsession with OCaml. It doesn't really solve any of these issues. It just forces you to write your program in a different way. The author decided to use static dispatch for the performance benefits. However, rust with dynamic dispatch would still be much faster than OCaml.
    Also, the biggest issue that the author made was assuming that static dispatch would improve performance. Generally, this is true, but depending on the specific use case the improvements could be negligible.
    Using Arc is ok. Using Clone is ok. Using dyn is ok. Never optimize before profiling.

    • @taragnor
      @taragnor Рік тому +3

      Yeah trying to overoptimize is a mistake people seem to make a lot. I mean, this thing is doing IO with a database, it's not some game engine trying to get more frames. The cost of making it a dynamic dispatch is going to be negligible. Chasing 100% optimization is rarely worth it.

  • @AndrewBrownK
    @AndrewBrownK Рік тому +30

    ....I'm afraid to say I've wrangled much worse in Rust. There is a lot you can do to compose traits and fold away the complexity. Composition is the correct tool to fold up complexity, assuming you can factor things correctly.

  • @varshneydevansh
    @varshneydevansh Рік тому +75

    Dude releases videos faster than Netflix series.

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

      that not hard, as we get a new show 2 times per year, in my country

  • @razielhamalakh9813
    @razielhamalakh9813 Рік тому +132

    This is where you use a tag trait specifically so you don't have to repeat the million where clauses every time. Skill issue.

    • @nevokrien95
      @nevokrien95 Рік тому +14

      I was thinking God isn't there a macro or something

    • @jesustyronechrist2330
      @jesustyronechrist2330 Рік тому +19

      @@nevokrien95 God should create a macro for it.

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

      What's a tag trait? Google doesn't show anything but the docs for traits

    • @xvxvxvxv7704
      @xvxvxvxv7704 Рік тому +11

      ​@@guguluduguluit's a trait that acts as a tag. You define an essentially empty trait that requires all those constraints then you constrain by requiring things to have this trait implemented.

    • @razielhamalakh9813
      @razielhamalakh9813 Рік тому +10

      @@guguludugulu xvxvxvxv is correct, I just mean a trait that serves as a tag. It has no methods, a bunch of trait bounds, and has a blanket impl for the types with those trait bounds. In that way, requiring that trait in a trait bound will automatically imply all those other trait bounds for free.

  • @maninalift
    @maninalift Рік тому +27

    I don't know rust, but I'm pretty sure there is a way to not repeat all those traits bounds. Like define one trait that depends on all the rest, or use a type alias.

    • @Xevion
      @Xevion 11 місяців тому

      Anyone who knows Rust better than OP or I able to confirm? Really curious.

    • @CamaradaArdi
      @CamaradaArdi 11 місяців тому +4

      ​@@Xevion really easy.
      The syntax `trait Foo = Send + Sync + 'static;` is nightly, but you can do
      trait Foo: Send + Sync + 'static {}trait Foo: Send + Sync + 'static {}
      impl

  • @porky1118
    @porky1118 Рік тому +14

    8:00 There are things you could do to avoid this.
    1. Define trains for all of these lines, which are automatically implemented on everything
    2. Make a trait ("Traits") to include all these traits (trait Trais { type A: AbusePolicy + Clone + ...; type C: Clock + Clone ...; ... })
    3. type SimpleDriver = Driver;

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

      Thank you. It's so obvious, but I had not thought about doing it this way.

  • @4445hassan
    @4445hassan Рік тому +12

    Solving this is actually quite simple. Define a trait `IsDriver` implement it for driver if and only if all the where clauses hold. Use `where Driver: IsDriver`. That makes it a lot better and it's not that odd of an idea i think.

    • @ThePrimeTimeagen
      @ThePrimeTimeagen  Рік тому +7

      you can constrain traits to methods too. hashmap does this

  • @SvetlinTotev
    @SvetlinTotev Рік тому +17

    Just have a named thing that represents the repeating stuff.
    It's the general solution to avoiding repetitions. People figured that out more than 10000 years ago. And most languages allow you to name most things.
    In this case Rust does have a way of naming this mess by creating a trait to contain the generic vomit. So the API is only exposing an object with a trait for that object. Even if you end up chaining many APIs, at the highest level you would still end up with a small number of traits that contain all the messy stuff inside them. But that applies to everything else as well. You write a single word to call a function that may contain millions of lines of code. You use a single word to create an instance of a class that may contain millions of members in the tree of objects it contains. But you don't care about what's inside. You just care about the interface.

    •  Рік тому +1

      Exactly. Although nothing prevents you to go crazy spreading the generic vomit all over the place. It does not mean that you have to. A trait per generic vomit could be an option here.

  • @Raspredval1337
    @Raspredval1337 Рік тому +7

    in c++ there's a dirty trick to 'solve' the template constraint thing: make an abstract interface and then check if the type you require derives from that interface. It doesn't have to make virtual calls, since you use the derived type as is, and it's sort of simple to migrate from dynamic dispatch to static. And since you can combine interfaces, checking if your type corresponds with the exact behavior you need is trivial: *template requires std::derived_from*, or even *template*

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

      i see elden ring, i like comment (even if i didn't read it)

  • @belst_
    @belst_ Рік тому +33

    doesn't the first #[async_trait] introduce dynamic dispatch instantly again, making this whole thing kinda moot?

  • @quettle
    @quettle Рік тому +23

    wouldn't something like a helper trait clear a lot of the noise? in the like of
    trait C: A + B + Clone + Send +Sync+ 'static{}
    impl C for T where T: A + B + Clone + Send +Sync+ 'static {}
    then it is just
    fn foo(x: impl C)

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

      exactly

    • @AK-vx4dy
      @AK-vx4dy Рік тому

      @@AndrewBrownK It is called supertraits .....

  • @remrevo3944
    @remrevo3944 Рік тому +25

    8:50 Yes, this is ugly and I would love it when Rust trait bounds would propagate automatically, but until that is implemented it is actually possible to use a mix of super traits and generic trait implementation to basically define trait aliase.
    That way the bounds wouldn't have to be copied and changes would be much easier to do.
    So yes it is ugly and it would be nice if Rust had better support for these cases. But there are solutions!

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

      What about macros? The pattern itself seems simple enough that you would only need a few parameters for the identifiers. Or am I missing something

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

      ​@@harrytsang1501 Normal macros-by-example don't really work with trait bounds.
      At least not well.
      There is just no token to express trait bounds.

    • @CamaradaArdi
      @CamaradaArdi 11 місяців тому

      You can just use a trait tag to avoid all of that copying.

  • @AbelShields
    @AbelShields Рік тому +3

    I see a lot of people saying "you can simplify it with a supertrait that combines all the others"... There's yet another solution too, why not just put all the possible drivers in an enum? Sure, there'll be one extra branch compared to the completely template solution, but:
    * it's easily extensible
    * you can use a crate (I forget the name but I know it exists) to automatically implement a trait for an enum where all the variants implement the trait too, so you can just use it like a driver with no source code changes
    * if driver doesn't change at runtime, all the branches will be predicted anyway, the overhead will be minimal
    And you can wrap up all the trait bounds required just at the enum definition!

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

      This is the comment I was looking for! "Dynamic" dispatch doesn't have to be a magic abstraction like a trait object, you can just make a mini abstraction to solve your usecase without losing most of the benefits of not using runtime type objects.

  • @christopher8641
    @christopher8641 Рік тому +6

    struct definitions should (almost) NEVER contain bounds on generics. Trait bounds define behavior, where a struct is a definition of memory layout. The moment you put a bound on a struct... you are obligated to include it on every impl block. impls define behaviors, not layout. Behaviors are defined by their bounds. I say "almost" because some marker traits (I believe just "Sized") makes sense to include on a struct, because Size is closely related to layout rather than behavior. Somewhere there is a github issue of David Tolnay arguing to include a lint in clippy for this exact thing. And tbh, that lint would have saved me soooo much time when first learning rust.

    • @joeybeauvais-feisthauer3137
      @joeybeauvais-feisthauer3137 10 місяців тому

      You might need bounds for the contents of your struct to even be defined. Something like
      struct Foo {
      bar: I::Item,
      }

  • @Siniverisyys
    @Siniverisyys Рік тому +3

    I interface with a large corporation that implemented their services in Go, using Rust on my end. I can say that the holes in their Go implementation are glaring. Don't use Go for an REST API type thing. There are all kinds of logic errors that Rust would've prevented

  • @alexpyattaev
    @alexpyattaev Рік тому +3

    In server code to avoid lifetimes just wrap your stuff in Arc. Or any other smart pointer of your choice. In IO bound context like web it would not matter and you have no silly lifetimes.

  • @dantenotavailable
    @dantenotavailable Рік тому +4

    I'd argue that sharing a transaction is just hard regardless of the language. Especially when it involves some kind of distributed commit bollocks that i'm sure works fine except when it doesn't (which, spoiler alert, is exactly the cases where you care about it in my experience). In the end, having to use multiple transactions in a code base (or module depending on your architectural bent) seems like a huge smell to me. Having said that just like all code smells, that doesn't mean it's not actually necessary.
    Also, I haven't dug around the III-IV code enough but I wonder how much of this is just down to things being included that shouldn't be the database driver's problem. Not that i'm ragging on the guy if that is the issue... keeping a design clean like that is a herculean task. Also, i'd probably set the general rule to be using dynamic dispatch and save static dispatch for places where it really matters which i'd argue it generally doesn't if your context is Web API endpoints talking to a database. You'll spend longer on protocol management and Db IO than you ever will on dynamic dispatch. (which is pretty much what you said)

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

    Prime is saying the same shit about the camel, previously said about rust! He is just a JavaScript technician that works for Netfix!

  • @RenderingUser
    @RenderingUser Рік тому +18

    apparently, this is just a skill issue

    • @mikkelens
      @mikkelens Рік тому +4

      its so interesting to me that people think "I want this complex ass thing to be perfect at compile time" and then do 1) the most naive solution possible, 2) spend no time making it better, then 3) revert everything and praise the original design. Happy for the developer that their codebase is better, but seems like they didn't even know what they wanted. Nonetheless, playing with rust types is fun so I don't blame the naive/intuitive refactoring approach.

  • @Kazyek
    @Kazyek Рік тому +3

    This isn't inherently an issue about static dispatch at all.
    However, doing static dispatch right AND ergonomic is pretty hard in some circumstances like this, and to have a truly ergonomic solution you need to either dive into the rustnomicon and carefully write some unsafe bits, AND/OR get to writing some macros.

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

      there is also a way to handle bits on a per method basis, thus you get less of the inconveniences

  • @sub-harmonik
    @sub-harmonik Рік тому +10

    I have no experience with rust, but could you not have an interface (or 'trait'?) that was declared to extend those other traits? seems like you could and just declare that trait instead of having to list every trait it 'extends' in every function that uses it. Even if it's a generic. I don't get it

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

      That would be the JEE way of doing it and everyone knows thats bad. Especially people who dont write JEE... :p

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

      100% true

  • @trondenver5017
    @trondenver5017 Рік тому +3

    18:30 successful inlining increases execution speed at the expense of a larger binary. Respectfully, the author of the article got it backwards.

    • @SimonBuchanNz
      @SimonBuchanNz Рік тому +3

      Often inlining will be for a single use, which results in sightly less overall code (a few bytes for the call and function prologue), and the real value is when the inlining lets the optimizer take advantage of local conditions to simplify the inline code, so it's not always an increase in size.
      But you're generally correct!

  • @krumbergify
    @krumbergify Рік тому +6

    It’s the same with templates in C++. Using dynamic dispatch (virtual methods through interfaces) is often easier, cleaner and compiles faster even if it has a runtime cost. Don’t invoke a dynamic dispatch method to write a single byte, instead pass it a whole buffer and the vtbl-jump penalty becomes negligible.

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

      Totally agree, this issue is more related to bad code design rather than Rust limitations

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

      Yes, it is bigger not smaller (but faster in runtime). However in C++ you can use an interface (just for the type, no virtual methods) and then set a requirement in the derived template. All static checked and super easy to implement with respect to Rust.

  • @conando025
    @conando025 Рік тому +6

    Why not create new super Traits like `Trait A: Clone + Send + Sync {}` You would still have to specify it but surely writing where `B: A` is less atrocious. Am I missing a downside of such an approach?

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

      It could be an issue if functions do not share trait bounds aka function 1 needs traits A B C but function 2 needs A B D. Composition can still help, at the very least in the example code & what we can infer their codebase to look like with the clauses "repeated". A different issue (that probably isn't what the developer was facing) could be that you may want additional/different function-specific constraints per function for preventing misuse of the function, though in rust this is typically more of a stylistic choice than a need, since this prevention of code smell is better solved using the compiler as a way to get the naive solution and then using lints or reviewing your code to optimise later.

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

    I have seen in the comments way to massively simplify the issue, but rust is also working on implied trait bounds which would eliminate the need for most where clauses in this code

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

      yeah Rust could really use some more inference tools where you can just type a keyword and just tell the compiler to figure it out. Even if it was just a relative type definition, that would be good. Like "typeof Argument1(f)" that way if you ever changed the type of f's argument, it would propagate.

  • @disguysn
    @disguysn Рік тому +3

    My biggest problem with Go is the error handling. If it had pattern matching like Rust I'd probably use it more.
    Due to your shilling, I'm going to have to check out OCaml.

  • @4445hassan
    @4445hassan Рік тому +3

    Also in most cases the correct answer would be to use an enum if what you are developing is not a library. Even if it is a library there is a real benefit to put all the variants you define yourself into an enum and having an `Other` variant which contains the `dyn`.

  • @tacticalassaultanteater9678

    That's why you don't pass the DI struct around directly; you define a trait, turn all the parameters into associated types, implement it, and then pass down an `impl FooContext`. This is symmetrical to how you'd define DI in C# or Java, only without dynamic dispatch.

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

    Is there any world where the speedup from static dispatch to relational database calls is relevant when compared to the time spent in IO for those calls?
    Asking for a friend…

  • @GigachadRustacean-lu8bo
    @GigachadRustacean-lu8bo 3 місяці тому

    if you would go for the static dispatch , and utilize a bunch of super traits , it is better to create macro to avoid the disaster of having to write all implementations. If u wanted performance then let the compiler do the work for you… at the expense of longer compile times, but you can opt to expand those macro later on if u wanted to…

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

    The feelings about rust have changed so much on this channel

    • @mikkelens
      @mikkelens Рік тому +4

      Prime is a pretty dramatic guy. He couldn't stop shilling Rust when it was new to him and now he can't stop shilling the new shiny thing he can't see negatively. Now that he's over Rust as the One True Language he can view it more realistically, but goes a bit further and views it negatively to contrast it with his feelings for other languages. To me what makes Rust shine through his perspective only is how much he has to admit it's still super good despite it maybe not being deity-status, contrasted with how he doesn't really talk about zig that much anymore since trying/"moving onto" OCaml

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

    i use cpp if I need maximum performance, rust if I need high performance or I want to use rust, and c# if I don’t care about performance at all. But inevitably I always regret using c# because i want more performance later down the road

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

    Leaking the where block to the REST layer is the bigger problem.

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

    Untyped null is now considered a billion dollar mistake, in the future type metaprogramming will also be considered a billion dollar mistake.

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

    That driver definition makes OO look good

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

      this is what OO would look like if it didn't have composition or types (aka not OO). The comparison doesn't work! I know you were probably joking, but I wanted to agree before thinking about it lol

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

      @@mikkelens yes I was joking

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

    Ultimately it seems like the only reason they need to change the database is so that they can use sqlite in tests, apart from that, using just Postgresql is fine. I hate to be the TDD guy, but this is the issue with testing after writing the code, you can build something that is so tightly coupled to a system that shouldn't be in tests that it turns into this when you actually get around to testing.
    If this was built up with tests using sqlite and units using postgresql, piece by piece, it could have built up a must less leaky abstraction. It still could have become this, but the issues would have shown up sooner where it would have been easier to fix.

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

    Its interesting how "lets see" is your "I'm speaking further than I've read, I need to go back to catch up"

  • @ghosthaunting15
    @ghosthaunting15 Рік тому +16

    Tom is genius

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

    Isn't that where associated types come in!?

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

    Lol, I just did something similar this week resulting in 2x end-to-end speedup

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

    I wrote something like this like 2 weeks ago but ended up refactoring it just fine.
    But I do agree there's no clear way to refactor the number of generics in a thing both up and down in rust. That's super annoying and I hope something like trait bound alias can land.
    Before that some kind of macro can probably help in some situations.

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

      What do you mean by "trait bound alias"? Like the "type" keyword? I guess you can't use type for traits like you can for structs/enums (since they aren't types in the same way), but you can use trait constraints to bundle them together, and it feels pretty similar: "trait C: A + B { }"

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

    Just rewrite it in C++ lol

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

    Once put two days of work into setting up icinga... took me a day to get it running stable in docker (first time)... then i saw the configuration and dropped it almost instantly xD
    Sunken cost fallacy my ass :)

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

    22:08 Raw JS is super useful!!

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

    ...It's safe because it makes it impossible to try and do things that haven't already been done... the framework achieves UNLIMITED SAFETY despite being written entirely in gotos! It's perfect safety record is compounded by this decision as most developers refuse to even CONSIDER working on systems that use just one goto that's not next to a token called "while" or "void*". Arbitrary semantics implemented as macros are tolerated giving the framework infinite extensibility that's NEVER USED, further contributing to its overall safety.

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

    If you are trying to make something this complex, sharing transactions, extended traits used on several layers... Static dispatch is simply not the right choice. Don't make it sound like static dispatch its the only right way. Dynamic dispatch is actually the most common way how every other language does this. It is perfectly fine. Rust just gives you the rare option to create a static source from generic functions in case it is possible.

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

    I do appreciate rust on a personal level, I am not a rust shill, but please don't take this as a bad faith criticism. this is a genuine comment/question:
    I am relatively new to your channel, and you seem like someone who tries to see things in a logical way, but when it comes to rust, it seems like you want to see it fail. I'm curious to understand why that would be the case.

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

    Tom would've figured that shit out! Did u know that Tom is a genius?

  • @sodreigor
    @sodreigor Рік тому +4

    Are you alright Prime my dude?
    You look a bit tired in the video.
    Are you taking proper time to rest?

    • @ThePrimeTimeagen
      @ThePrimeTimeagen  Рік тому +6

      i am ! i have reduced my streaming 1 day less a week.
      the thing that is hurting me right now is teaching courses for front end masters

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

    Python rewrite, it'll take you 10 minutes and there's no compilation. Let's go!

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

    6:09 Is there no form of type aliasing in Rust?

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

    That absolutes line is an absolute

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

    There are some good comments at the end of the reviewed article I think. Would be curious about your thoughts on those comments!

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

    The way he talks is Bill Burresque

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

    You missed the obvious: That phrase is an absolute. Ergo, anyone using is outing themself as a Sith.

  • @CamaradaArdi
    @CamaradaArdi 11 місяців тому

    Massive skill issue.
    the syntax `trait Foo = Send + Sync + 'static;` is nightly, but you can do
    trait Foo: Send + Sync + 'static {}trait Foo: Send + Sync + 'static {}
    impl DbDriver for T {}
    trait TxDriver: Tx + Send + Sync + 'static {}
    impl

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

    > `Arc`
    Am I the only one who uses feature flags and conditional compilation to do mocking in Rust? I detest having to change the way my code is written just to accommodate mocks

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

    Not sure why the author thinks modern C++ is deranged but this is not. Rust is usually great, but I’ve never had to write monstrosities like that in C++.

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

    As a JS developer (primarily), yes, it does suck!

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

    8:51 Simply fix the ™ sign in the browser's developer tools, problem solved.

  • @0xCAFEF00D
    @0xCAFEF00D Рік тому

    5:50
    Not familiar with Rust but this kind of repetition could easily be solved with a C Macro :)

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

      trait composition (written like "trait C: A + C { }") is like trait 101 and its pretty interesting that the author doesn't mention it or isn't familiar with it

  • @АнтонФамолия
    @АнтонФамолия Рік тому

    Why even optimize dynamic dispatch when you are doing network call with it

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

    if you have to paste a assload of where statement 44 times why would you not include it in either a build script or a macro of some kind?

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

    Make a playlist for prime reacts

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

      ...?
      look at the playlist this is on!!! its on prime reacts!

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

      ​@@ThePrimeTimeagenanotha one

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

    What if I am also a child?
    Only a sith deals in in absolutes.

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

    This is the worst thing about Rust in my experience, its a leaky language, in the sense that implementation details of your types and modules easily explode into a mess across the entire program.

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

    “I tried a day of java and java sucks but javascript is great”. That hurts to hear even as a strawman.

  • @azratosh
    @azratosh Рік тому +3

    big skill issue right there, use associated types

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

      you can also constrain the method to the trait required like hashmap does (you don't need a bunch of traits to define all its behavior, just on the methods you use)

    • @azratosh
      @azratosh Рік тому +3

      @@ThePrimeTimeagen Very good point! Though that being said, if your trait has a method that takes a generic parameter, it won't be considered object-safe anymore (so, no Arc).
      I'd rather prefer something like this:
      trait Foo {}
      trait Bar {}
      trait Baz {}
      trait Qux {}
      trait DoesItAll: Foo + Bar + Baz + Qux {}
      impl DoesItAll for T where T: Foo + Bar + Baz + Qux {}
      Then you can just do a "where T: DoesItAll" on your generics and boom, you've reduced the where-clause-boilerploooting.
      Rust does actually provide a lot of ways to reduce complexity, but unfortunately those are hard to come by when you've already coded yourself into a corner.

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

      ​​​@@azratosh
      Your method is good and it has its place, for example when you do have unsafe code and you need to uphold some invariants no matter what. But for that use case it might be an overkill.
      About the type safety of constraining methods to traits. Rust will not let you exhibit behavior (call a method) if traits are not satisfied (for example, call `.clone()` if trait is not `Clone`, so it is absolutely type safe to, for example, have a struct that should be `Clone` but to not restrict it's generic bounds at the definition points, but rather implement `Clone` where all generics are `Clone`. Then, at the point where you need it to be `Clone`, constrain. That will save a lot of boilerplate.

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

      @@ddystopia8091 Oh yeah, totally. I would never constrain the T on a Struct directly (unless really necessary); only on the impl blocks. Keeps everything tidy and modular.

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

    too much videos, quality is meeeh. Was nice to follow you for a time. You gave some good advice!

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

    Couldn't the duplication not handled with a Macro?

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

      25:19 apparently not

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

      @@bary450 Oops, not sure how I missed that part. Thanks for pointing it out.

  • @js-ny2ru
    @js-ny2ru Рік тому +1

    You don't like Rust anymore Prime?

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

    There is a solution! Just a trait wrapper! Really!
    Example in my comment below (hope UA-cam didn't ghost-remove it)

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

      pub struct SimpleDriver {
      driver: X,
      }
      impl SimpleDriver {
      pub async fn simple_run(&self, cmd_params: CommandParams) -> Result {
      self.driver.handle(cmd_params).await
      }
      }

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

      // part2
      #[async_trait]
      pub trait DriverTrait {
      async fn handle(&self, cmd_params: CommandParams) -> Result;
      }
      #[async_trait]
      impl DriverTrait for Driver
      where
      F: Fn(CommandParams) -> T + Send + Sync + 'static,
      T: Future + Send + Sync + 'static,
      {
      async fn handle(&self, cmd_params: CommandParams) -> Result {
      let result = (self.inner)(cmd_params).await;
      result
      }
      }

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

    D's openmethods = problem solved 😎

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

    My hot take is there's no such thing as a zero cost abstraction

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

      The whole reason to use C++ is that it offers many zero-cost abstractions.

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

      I'll get you one better: there isn't anything that is universally true/perfect for everything, it's just a matter of tradeoff scale along with what tradeoffs you care about and what tradeoffs you don't.
      also @agcwall misunderstanding the original comment and acting like c++ is uniquely offering Perfect Zero-Cost Abstractions in this regard is pretty funny considering the subject matter

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

    7:53, can't do macros for that?

  • @AK-vx4dy
    @AK-vx4dy Рік тому +1

    This article again?

  • @jboss1073
    @jboss1073 Рік тому +3

    OCaml shilling is NOT annoying.

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

    `trait Db: Send + Sync {`

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

    My eyes

  • @AK-vx4dy
    @AK-vx4dy Рік тому

    @9:30 This where could be shorten and DRY-ed by supertraits...
    Guy don't know Rust well and trying to implement concepts from another languge.
    I saw the same with guy who was c++ programer and make presentation about how he tried to port his app to rust.
    When he wrote that sql driver spoils rest endpoints that was clear that design is poor and/or cargculted from other langage

  • @Robert-zc8hr
    @Robert-zc8hr Рік тому

    TBW, I still think code generation is the way.

    • @Robert-zc8hr
      @Robert-zc8hr Рік тому

      But at compile step, where the generated code is the output of a library then then you import from somewhere else.

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

    Forth. Thanks for the recommendationt to practing coding typing! super helpful!

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

    Should have used a macro to automate that where block.

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

      Unneeded macro! Just do trait composition/self-constraints: "trait C: A + B { }"

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

    this sounds like a case for a macro

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

    This is one the issues I have with adding any constraint, it just infects every piece of code that the type/trait is involved with.

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

    Are there good lanugages with async/concurrency without red blue function distinctions? (other than go)

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

      whats red blue function distinctions?

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

      @@RenderingUser JS and Rust have regular and async functions. You can call regular functions from async but not vice versa. Distinction with weird asymmetry.

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

      @@gritcrit4385 I see. But is that much of a problem tho?

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

      ​@@gritcrit4385you *can* call sync from async, but you need to be careful it won't block, and you *can* call async from sync, but not then get the result without higher level coordination.

    • @sub-harmonik
      @sub-harmonik Рік тому +1

      I believe zig has that ability (when the async stuff is actually included in a release lol)
      coroutines in lua are made from normal functions w/ the ability to yield from any function in the coroutine (anywhere in the stack), not sure if that counts.

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

    11:46 Just go back to dyn, not GC!

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

    Just make type aliases

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

    twenty ninth?
    Edit: Fixed grammer errors

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

    Elm is better than TypeScript it is a lot more ergonomic

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

    yes, All of us are :(

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

    So much time spent on Unreadable types when you could just manually write a codegen build step in less time. C doesn't have this problem

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

    NaN?

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

    Child exploitation is wrong absolutely therefore I'm a sith and proud of it. The "dark side" of the force allows one to shine more brightly imo. Good vid PrimeTime. Ethical appeal 11/10.
    From the vid I see that setting up domains and policies are the real issue where Go and others are merely a way to develop (like DB transactional interfaces) without getting involved with policy definition which is a double edged sword; laziness being the edge facing the wielder. Ignorance regarding Rust policies is valid but understanding one's own code and policies regarding interfaces would make it far easier to query the community and documentation relationally, so...
    Not advocating for or against Rust and others but many arguments/bullets seem like a one and done throwing in of the towel many times regarding Rust development. Tech officers and testers are generally going at languages with a bit more openness and curiosity as well as a deeper understanding of things of such a magnitude require the adequate level of consideration opposed to a quick "deep dive" considering something as monumental as a total migration. Looking back it feels as though I'm regurgitating what you've stated and wonder if those "devs" are listening to your advice because you sound familiar to a CTO. Good stuff.

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

    Please more OcaML shilling

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

    There is no reason to have written any of that program in Rust at all. I appreciate the implementation on an exploratory basis, but he could have slapped this whole thing together in 30mins or less in Node, and given hes bound by IO throughput to a DB (and running transactions) performance would be basically the same (maybe a bit more overhead on the memory side for Node, but who cares). Genuine question, why the hell are developers punishing themselves trying to use Rust for application level workloads.....if people are that hard up for performance, just write the performance critical stuff in Rust, then write an interop binding for Node (or Python, Go, C#, whatever) and carry on. Rust is good, but it's a square peg in a round hole for application dev.

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

      For me, it's that portable client apps don't really want to lug a 100MB runtime around before they even start adding their own code, and when you're doing desktop apps in general the error handling in general is much more reliable feeling (and in general has been much more reliable than our node and browser code, but there's confounding factors)

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

      @@SimonBuchanNz Node is 68MB (which is non-zero, but negligible in 2023 in terms of executable size). Initial overhead to run a single Node process (v20/Windows) is 6mb (non-zero but still negligible)
      The claims made about Node overhead are largely exaggerated. While it's true many Node packages are written inefficiently (I try to avoid using most stuff off NPM personally).... if you're prepared to sit down and write some low level Rust in order to achieve result, you might as well spend that time writing low level (relatively speaking) Node code that layers the core IO API's (as you're liable to get much better performance than dealing with abstractions off NPM)
      To me, it seems an extraordinary waste of time and energy to expend efforts authoring processes in Rust that could trivially be authored in Node (especially when the trade off is a few MB here and there)
      This isn't to suggest Rust doesn't have a purpose, it's an awesome language, it's just that 99.999% of applications do not need to run in such constrained environments to warrant the maximum effort required to achieve low memory, high performance native processes.... and even writing stuff in Rust doesn't assure high performance.
      I really think developers should try and weight the pros and cons of doing Rust for application level work. I get it, Rust offers some great features, but putting these to use in actual applications expends time, energy and cost, and may result in a statically inflexible application unable to accommodate for change.
      Sometimes you need a bit more dynamism at the application level.

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

      @@BinaryReader as I've mentioned, we do have node client apps. I do know when it's an appropriate choice. Nowadays, it rarely is an appropriate choice for client apps. If you want to do JavaScript, embed deno in your own app or something similar (eg Tauri, which uses the platform browser apis), but if you're serious about having a stable application it's still not an appropriate choice, as I also mentioned.

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

      @@SimonBuchanNz What's not stable about it? If anything, JavaScript environments are typically backed by WC3 and IETF standards and there's those don't change (they fall under the "don't break the web" doctrine). As for Node, it's internals are also unchanging (for better or worse)
      On top of this, JS is usually made to run within secure contexts (as is the case for Deno ... but also platforms like Electron where applications can leverage 'Content-Security-Policy'. This extends to storage isolation if operating through Browser specific API.
      This is a lot of stuff to throw out or re implement or just pretend you don't need if attempting similar things in Rust....There's more to the application layer than just "lol, look at the static dispatch", you still have to wind up with an application at the end of the day, and you gotta tick these boxes at some point (or at least have the option to)
      Not saying it's not possible, but why expend the energy to only wind up with something proprietary and at odds with established specifications?
      Not to suggest I've got some wider field of view than other developers, but developers do tend to tunnel vision and focus on solving "narrow issues" and miss the bigger picture. Rust is generally inadequate at the application layer (imo), and while all of these things are technically possible, you could count the number of developers able to architect a competent solution in the language on one hand.
      This isn't the case for JavaScript, C# or Go (or any managed language that provides infrastructure above and beyond a GC)

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

      @@BinaryReader because of the ethos of the language and community expectations around library design, and just the general age and size of the community, it's harder to write applications in a "never crash" way, or at least meaningfully handling the issue.
      I've spent years doing this in both Node and Rust. I often end up needing to rip out the npm package and drop down to lower level packages, or patch them, or vendor and basically rewrite them. Even when they're good, they often have massive breaking changes and need a bunch of churn that can cause other issues. I'm in the middle of such churn right now.
      My Rust code, including the stuff I wrote as a complete Rust neophyte from years ago, has worked perfectly and hasn't needed many fixes, despite often making changes.
      This isn't, as I've said, completely fair, considering the size of the code and so on, but it has certainly felt a lot nicer and easier to get reliable code in Rust than Node. And *much* more fun.
      Basically, programming always sucks, but Node sucks more than it should.

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

    Second?

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

    Красава супер видео

  • @randomperson6749
    @randomperson6749 Рік тому +6

    First?