The genius of Rust constructors

Поделиться
HTML-код
  • Опубликовано: 19 ноя 2024
  • НаукаНаука

Комментарии • 247

  • @letsgetrusty
    @letsgetrusty  Месяц назад +11

    Get your *FREE Rust training* :
    letsgetrusty.com/bootcamp

  • @toast_on_toast1270
    @toast_on_toast1270 Месяц назад +57

    C++ doesnt force you to do anything, and in my opinion, thats the problem.
    If you want a return value from the constructor, you can do it. First make a static member 'create' function returning your std::optional or whatever, put the failure-prone logic in this function and then copy/move whatever is needed into the constructor of your class. The compiler can decide to use NVRO so you dont have to worry about performance overhead. Then just make the constructor private so users know to use your builder instead.
    Many people do not think to use this trick, which brings me to the real problem with c++: that it *doesnt* force you to do things a particular way, leaving way for inconsistency, misuse of APIs, avoidable mistakes and bad design choices.
    So, I like that rust embraces a more prescriptive approach.

    • @letsgetrusty
      @letsgetrusty  Месяц назад +2

      Yes that's a great summary of the core issue!

    • @ciamej
      @ciamej Месяц назад +2

      That’s the code pattern I always rely on, but it feels kind of unnatural in C++ forcing me to both create private constructors and public static ‘factory’ methods.

    • @toast_on_toast1270
      @toast_on_toast1270 Месяц назад +5

      @@ciamej yeah, well almost everything in C++ is more than a little idiosyncratic. Its a language that has persisted through so many paradigms and movements in its continual development. It tries to offer everything, and always favors expressiveness over conciseness and ease of use.
      Consider this example function declaration: "[[nodiscard]] auto get_data() const noexcept -> std::span;". To most people, this is a nightmare (I even decided to spare you the 3x increase in verbosity from the added template stuff this code would often have). To me, it's beautiful, because it expresses exactly the intent behind the function, namely that it returns a view to some data, has no side effects, cannot throw an exception. But even I have to admit, a header file filled with so much visual clutter makes you wonder if there isn't a better way (perhaps by making these modifiers and attributes default, because they should be).
      It's weird because, at this point, correct usage of the language (imo - see Jason Turner's POV on this) necessitates subscribing to all of this ritual, a bit like joining a cult. It seems like Rust, more than anything, tries to make these modern considerations idiomatic, without having to support all of the idioms from the previous half century. I have yet to dive into it, but I'm hoping that Rust is the beautiful language C++ wants to be, but cant.

  • @c-antin
    @c-antin Месяц назад +15

    Great video! One thing to add: you can initialize a struct partially by passing `..Default::default()` to the struct initialization.

    • @landonwork675
      @landonwork675 Месяц назад +2

      This only works if your struct impls Default

    • @Mankepanke
      @Mankepanke Месяц назад +2

      ...which then must be able to initialize everything.
      So this is mostly sugar for creating a struct instance, then overwriting a field with a new value. (But the compiler can remove the redundancy)
      You still cannot create an uninitialized field outside of unsafe rust or by opting in to it with MaybeUninit.

    • @Spiker985Studios
      @Spiker985Studios Месяц назад +1

      The nit-pick wasn't that you could create an uninitialized field, it was that you can create an object with only partial default values - not being limited to using the default implementation wholesale

    • @yondaime500
      @yondaime500 Месяц назад +4

      The ".." syntax in this context just means "if any fields are not mentioned above, take them from this other fully initialized instance". Passing Default::default() is the most common use case, but you can actually put any expression that evaluates to an instance of that type.

    • @Mankepanke
      @Mankepanke Месяц назад

      @@yondaime500 TIL. Thanks!

  • @OMGclueless
    @OMGclueless Месяц назад +33

    Your example at 2:03 does in fact call the Employee override of introduce(). The Base implementation of introduce() can only be called _by the Base class constructor itself_. This is still sometimes confusing, you might expect that any Base class functions that are overridden are not callable at all, but it's not quite as alarming as your example suggests -- code in Example's constructor will always call Example's methods.

    • @enigma_dev
      @enigma_dev Месяц назад +3

      Good catch. I think they meant to show the opposite.
      The Base class calling a virtual method in the constructor. (ie Person() calling Introduce();)
      In that case, the child virtual override will not be called, basically because child isn't constructed yet, while the base is being constructed.

    • @enigma_dev
      @enigma_dev Месяц назад +1

      Made a compiler explorer example to show the problem of calling virtuals in base classes: /z/zsaTY73jY

    • @ХузинТимур
      @ХузинТимур Месяц назад

      Yeah, vtable pointers are overwritten before calling a constructor. E.g. if we have classes Parent and Child, there would be such steps:
      1. Write pointer to vtable of Parent to hidden pointer.
      2. Execute Parent ctor.
      3. Write pointer to vtable of Child to hidden pointer.
      4. Execute Child ctor.

  • @proficiency03
    @proficiency03 Месяц назад +21

    You can make a series of Rust projects. All the way smaller to more bigger and complex projects.

  • @MrCyanobacterium
    @MrCyanobacterium Месяц назад +6

    The issues mentioned about C++ constructors are not an issue in Java or C# (except the part about throwing an exception), so these aren’t OOP problems but a C++ problems

    • @danilmiroshnikov6130
      @danilmiroshnikov6130 Месяц назад

      this. idk why author really mention that. Anyway, it's obvious that any language would be any way possibly perfect in its core principles/scenarious like creating objects. Imo, the difference or what makes language a language is the whole API ecosystem. As a Java dev I can tell he one it has is about business features, which is nice for some things and bad for others. And the same goes to any other lang, including Rust.

    • @MrDavibu
      @MrDavibu Месяц назад +2

      Exceptions have to be handled in Java, so it's the same as the match in Rust.
      Quite a lot of bad faith arguments in this video tbh.

  • @amynagtegaal6941
    @amynagtegaal6941 Месяц назад +2

    I wasn't really aware of that C++ quirk explained in the beginning..
    But now looking back at my code i see that i already thought about it unconsciously and implemented a work around... I guess i was already aware of this while not being aware of being aware of it???

  • @clementdato6328
    @clementdato6328 Месяц назад +2

    One problem with this is upstream APIs will become constrained: all extra parameters are breaking changes to the downstream. There are thus new/builder patterns to mitigate this. This automatically forces library providers to boilerplate many of their types with such patterns to ensure further extensibility.

  • @jongeduard
    @jongeduard Месяц назад +2

    Note that there are still better design practices possible in C++ too, by following a more rustacean approach. 🦀
    From examples I what see people doing is using a single struct with multiple fields inside it, used as the only member of a surrounding class, and then initialize that as a whole from the class it's constructor behind the colon, so that it has to be created all at once. Because in that way C++ constructor syntax does actually enforce it.
    For additional work after construction the factory pattern 🏭 should be used, which is basically what new() methods in Rust are too. Avoid any work inside a C++ constructor body.

  • @carterthaxton
    @carterthaxton Месяц назад

    The Rust naming convention for traits would be to call it “Introduce” instead of “Introduceable” (which should really be spelled “Introducible” anyway). At first it seemed foreign to me, but I’ve come to absolutely love this convention.

  • @blt_r
    @blt_r Месяц назад +85

    One downside of rusts approach is that new objects must be returned from functions, so they can only be constructed on stack. This is bad for big objects that you can't fit in the heap. You can make a constructor that returns a Box, but to create it with Box::new you will still have to pass the object by value through stack (with unstable box keyword it also isn't guarantied that the object will be directly constructed in heap and not copied there from stack). It's also commonly wanted to construct an object in already allocated place (like placement new, or vector::emplace_back in c++). Right now, this cant be done with safe rust, because it will necessarily involve passing a pointer to an uninitialized object.

    • @delir0
      @delir0 Месяц назад +42

      I can't imagine a single real world case where it is a downside

    • @irrelevantgaymer6195
      @irrelevantgaymer6195 Месяц назад +23

      Then write it in unsafe rust

    • @TheShynamo
      @TheShynamo Месяц назад +15

      @@delir0 For example, when developping a video-game, you need a lot of performance because FPS is critical.
      Allocating memory is expensive, so the game engine pre-allocate a huge chunk of memory, and then lets you store and load objects within it without having to do a system call, which increases performance (FPS).

    • @anderdrache8504
      @anderdrache8504 Месяц назад +11

      @@delir0 it overflows the stack when constructing large objects

    • @raykirushiroyshi2752
      @raykirushiroyshi2752 Месяц назад +5

      ​@@TheShynamo why write games in rust... You might as well use Odin if you don't wanna use c++.
      If you do want performant application,you will have to use unsafe rust

  • @v-sig2389
    @v-sig2389 Месяц назад +1

    I'm not even an unconditionnal fan of c++, but the way you demolish its constructors makes me want to take its defense 😂

  • @leftaroundabout
    @leftaroundabout Месяц назад +5

    So, how is this unique and any more genius than how Haskell has done it since the 90s?

    • @pmmeurcatpics
      @pmmeurcatpics Месяц назад +4

      The advantage of Rust is that it's actually being used. And I don't say this as an insult to Haskell, or FP in general - it's just a common problem that they, while being very nice theoretically, tend to feel very unapproachable for people without PhDs

    • @leftaroundabout
      @leftaroundabout Месяц назад +3

      @@pmmeurcatpics that's the common argument, and it's a fair one (though Haskell and O'Caml are definitely not mere theory but fully capable languages).
      My complaint is that features are presented as unique accomplishments of Rust when in reality they have been a thing long before Rust came around and the mainstream just didn't pay attention.
      Perhaps the Rust implementation improves over the functional languages in some ways, but then it should be said like that. It's not genius, it's just good work.

    • @ElektrykFlaaj
      @ElektrykFlaaj Месяц назад

      @@leftaroundabout I think what's genius about Rust, that it's really a language where the best features of all existing languages were taken and gracefully mixed into Rust, while still being a language with almost the performance of low level languages.
      You don't have to invent shiny new thing to be genius. You can just be wise and take what works and compile it into something that works even better

    • @leftaroundabout
      @leftaroundabout Месяц назад

      @@ElektrykFlaaj genō (Old Latin) to bring forth as *a fruit of oneself...*
      ...Anyway, I don't mind Rust being called genius, I mind the lack of attribution.

    • @henrycgs
      @henrycgs Месяц назад

      it isn't. it's pretty much a consensus in the rust community that haskell is great.

  • @realSimonPeter
    @realSimonPeter Месяц назад

    It’s interesting to see how different languages approach the same problems that arose with C++’s constructor design.
    Swift’s struct init() methods are the same as Rust’s static new methods, in that they can return an optional or throw an error (which must be handled by the caller at compile time, like a Result).
    However since Swift had to support classes and inheritance for backwards compatibility with Objective-C, it also had to deal with the fragile constructor issue to maintain memory safety guarantees.
    The way Swift does it is require you to initialize in reverse order: the derived class must initialize ALL its members before calling super.init(…), and it cannot call anything on self until that returns. Nor can it escape a reference to self or access any member other than the ones it already initialized.
    It seems obvious in retrospect but I think it only works because Swift and ObjC only support single inheritance.

  • @Pritam252
    @Pritam252 Месяц назад +1

    I like rust's approach to the problem, despite me trying to use workarounds for problems like I did in C/C++ and finding myself frustrated instead. 😂

  • @mahor1221
    @mahor1221 Месяц назад

    Thank you for the video!

  • @nevokrien95
    @nevokrien95 Месяц назад +1

    Idk if rust heavily prioritises simplicity. It's a VERY complex languge compared to something like python or C

  • @christopherpetersen342
    @christopherpetersen342 Месяц назад

    i've seen a few discussions of implementing both new() and Default::default() but making one call the other (in either direction). does that actually make an API more flexible?

    • @RoyaltyInTraining.
      @RoyaltyInTraining. Месяц назад +1

      Usually I'd expect new() to create an object with as few assumptions as possible, basically the opposite of default(). If we're talking about a person struct, I'd expect default() to return something like Person { name: "unknown", age: 0 }, while new() should take in name and age as parameters and move them into the struct.
      You could do this by making default() call new(), which might even be advisable if a struct takes a lot of logic to create. But in this case it would be simpler to just use the struct literal syntax directly in default().

  • @HinaraT
    @HinaraT Месяц назад +4

    02:11 wrong example but if you call introduce in the Person class the override version would effectively not be called as the vtable of Employee is only set once entering the brack of Employee's constructor and "company" field of Employee is missing a type....
    I love your videos but please try your code before giving an exemple :/

  • @dovonun
    @dovonun Месяц назад +1

    If I understand this correctly you can't have a function that takes a person or an employee and access the name by using `.name` on the struct. How does Rust work around this?

    • @jcm2606
      @jcm2606 Месяц назад +1

      You can if the `name` field is public. If it's private, the convention is to either create a getter function that returns a reference to the field, or to simply not allow external crates to access the field if it could potentially violate some invariant (since you can have fields be publicly visible within your own crate, but not visible to other crates that depend on your crate).

    • @dovonun
      @dovonun Месяц назад

      Interesting, i think this allows for some good abstractions. I have to play more with rust

  • @lMINERl
    @lMINERl Месяц назад

    I'd agree if someone told me its beginner mistakes if you get some of these errs in C++ but man the thing about rust you dont need to care about anything unless you are in unsafe block and thus far miri is doing a good job pointing out undefined behavior

  • @EmranAhmed08
    @EmranAhmed08 Месяц назад

    What editor do you use to move code block like 1:34-1:35 for tutorial?

  • @andreaselfving2787
    @andreaselfving2787 Месяц назад +9

    You can use (and should) warnings, and treat warnings as errors, to catch uninitialized values for cpp.
    I agree that the virtual function call in the constructor is a bit of a skill issue, and just a fact how constructors work. An object isn't actually constructed before the constructor has exited, which makes sense.
    Constructors do NOT "return a pointer to the object".

    • @michaelcummings7246
      @michaelcummings7246 Месяц назад

      But that is exactly what C++ does the difference is it gives you the option to have an incomplete object when done which is just shooting yourself in the foot.

  • @orterves
    @orterves Месяц назад

    Traditional constructors suck because the only failure mode is to throw an exception, so they need to be keep fool-proof, and more complex initialisation has to be moved out of the constructor - but in C# and others, constructors are the backbone of DI so it's hard to avoid them. But everything Rust does here is better architecture; a DI library designed around static new with Result would be an interesting topic

  • @bmx666bmx666
    @bmx666bmx666 Месяц назад +48

    I'm so glad I didn't spend my 10 years learning C++ in depth. And I am so glad that Rust has appeared in the industry.

    • @Kiyuja
      @Kiyuja Месяц назад +3

      same honestly, tho it sure has a hefty learning curve sometimes

    • @shosetsuninja3112
      @shosetsuninja3112 Месяц назад +2

      C++ is still heavily used in my industry. I can't imagine any of the long-time C++ experts being willing to switch. It would be safer for onboarding new engineers if they wanted to take this direction, but it would be a huge shift in how things are done.
      I hope I can be glad I didn't spend 10 years learning C++ and that more and more opportunities to do system level programming emerge for engineers competent in Rust too!

  • @elpitbullhouse
    @elpitbullhouse Месяц назад +44

    It's so sad that these basic functional programming features that exist in Haskell and other FP languages since the 90s are though of as "bold" and innovative in 2024 🤦‍♂️

    • @linkernick5379
      @linkernick5379 Месяц назад +13

      They couldn't unify Async, Option, Result and IO without GC.
      Actually no one in the whole world knows how to do it with deterministic memory management.
      This is the thing it is sad really, since monads and algebraic effects are very beautiful abstractions for declarative application programming.

    • @urisinger3412
      @urisinger3412 Месяц назад +1

      Rust doesn't have a gc

    • @avwie132
      @avwie132 Месяц назад +1

      @@urisinger3412 That is his point

    • @urisinger3412
      @urisinger3412 Месяц назад +1

      @@avwie132 that's what makes it special

    • @JorgetePanete
      @JorgetePanete Месяц назад

      thought*

  • @hoyoreverse
    @hoyoreverse Месяц назад +31

    And then OOP fans are still talking about "your rust is missing VERY IMPORTANT thing of programming that is called struct inheritance, how would I represent my [insert some common case] in it!". They probs never tried rust or played w/ it for a week and failed cuz they didn't even try to think different instead of trying to re-invent OOP everywhere

    • @sunofabeach9424
      @sunofabeach9424 Месяц назад +1

      6 months into rust, and single parent inheritance would benefit it alright.

    • @Traversed-g8h
      @Traversed-g8h Месяц назад +5

      @@sunofabeach9424 How so? Name a situation that couldn't already by adequately represented using current rust constructs.

    • @T1Oracle
      @T1Oracle Месяц назад +2

      You should look at the GTK Rust code. They actually made inheritance macros! 🤦🏽‍♂

    • @TheEvertw
      @TheEvertw Месяц назад +6

      Inheritance of anything except interfaces ('traits') is grossly overvalued. The current consensus is to prefer composition over inheritance, which is exactly the point that Rust makes.
      The problem is that semantically you can not predict what the result of inheritance will be like. It is easy to violate invariants for the base class in a child. That problem does not exist with composition.
      In many cases where inheritance would actually be useful, generics will do as well.

    • @dminik9196
      @dminik9196 Месяц назад

      ​@@Traversed-g8hWhen working with SQL the result you get back is often flattened. Eg, you might have columns like user_id, user_name, user_avatar_id, user_avatar_url. I've run into cases where I have some fields I will only query in certain situations and also have fields I will always query.
      Now, there's multiple ways to solve it, but they require support from the library (eg. It's missing from SQLX) or don't necessarily represent what I'm trying to do. Being able to say hey, this struct also has these fields from over here would be great.
      Maybe something like struct Thing { ..Other, I'd: i32 }

  • @Sergio_Loureiro
    @Sergio_Loureiro Месяц назад

    How does that constructor approach handle dependency injection?

    • @jcm2606
      @jcm2606 Месяц назад

      By taking in instances of or references to any dependencies as arguments. Factory functions, which is the actual name for Rust's style of constructors, _are just regular functions,_ so they can take in anything they want as arguments, including instances of or references to other objects. If your object depends on some other object, you can just have your factory function take in a reference to that other object and store the reference within your object.

  • @Victor-fq6lz
    @Victor-fq6lz Месяц назад

    Thumbnail goes hard

  • @TheEvertw
    @TheEvertw Месяц назад +17

    Rust has very cleverly understood that a Constructor is in fact syntactic sugar for an implicit factory function. But one with many pitfalls and syntactic problems, which are a direct result of the limitations of the chosen syntax. Then Rust decided that they can do without this implicit factory function, and instead use regular factory functions. BRILLIANT!

  • @spoomer94
    @spoomer94 Месяц назад

    5:11 but we didn't get the answser is it possible to call specific introduce function within constructor in Rust

    • @natew4724
      @natew4724 Месяц назад

      Use a temporary variable

    • @jcm2606
      @jcm2606 Месяц назад

      Construct an instance of the struct, bind it to a temporary variable, call `introduce()` on that temporary variable, then return that temporary variable to return the instance of the struct.

  • @ShredThatSchecter
    @ShredThatSchecter Месяц назад +1

    Not being able to call virtual methods from a constructor really limits the amount of things you can do in a constructor. This always seemed like a design flaw, which leads you to construct the object and then call a method to initalize things properly

    • @anon_y_mousse
      @anon_y_mousse Месяц назад

      If you think that, then you're misusing virtual methods. If you want a function to call from the constructor then make it private and definitely not virtual.

  • @DrSoftman
    @DrSoftman Месяц назад +4

    Nice!

  • @EE-cc5bd
    @EE-cc5bd Месяц назад

    Oh damn thanks. Well this video helped me discover that there is no in-place construct in Rust. Sad.

  • @vcciccv5917
    @vcciccv5917 27 дней назад

    Now I feel confused when building large web projects,c?an you explain how to use rust to achieve clean architectural code

  • @MT-xb3ts
    @MT-xb3ts Месяц назад

    Wow, your video animation is fantastically clear, what software do you use?

  • @dampfwatze
    @dampfwatze Месяц назад +3

    0:07 I love that he picked Dart as a well known language on his list! Dart is Fire!! 🎯🔥🔥

    • @davidowens9597
      @davidowens9597 21 день назад +1

      I agree. I like Dart, Go, and of course, Rust.

  • @hrobertson4
    @hrobertson4 Месяц назад

    I love all the amazing concepts Rust has invented for other languages to steal.

    • @leftaroundabout
      @leftaroundabout Месяц назад +3

      ...except, Rust stole most of them from earlier functional programming languages...

  • @RoyaltyInTraining.
    @RoyaltyInTraining. Месяц назад

    I never understood why C++ went with the constructor model it has now. The member initializer list makes having any kind of logic in the constructor excessively hard.

  • @Kiyuja
    @Kiyuja Месяц назад +10

    my jaw just dropped. Even after multiple months of Rust I had no idea this was a thing. My C#/Java brain always wanted to create objects with a New function but that didnt work on my own data types, so I always created structs and thought this is "the Rust way I guess", I didnt even think about creating my own function to return the data type...in this case with "self". OMG, I have to refactor some old code of mine 💀

    • @mk72v2oq
      @mk72v2oq Месяц назад +1

      Well, then I would say that you are not a very experienced C#/Java developer either. This is a very popular pattern across pretty much all languages called "Factory", Rust didn't invent anything here.
      I.e. C#/Java devs often do that too due to constructor limitations. For me that was primarily due to lack of async constructors.

    • @Kiyuja
      @Kiyuja Месяц назад

      @@mk72v2oq we use Factories exclusively for when you need multiple instances based on parameters, like HTTPClients or Identities for the DB.
      I also would suggest you arent experienced in reading either, as this wasnt even what I was talking about. I was talking about something like Person x = new Person(). And if you are telling me that you use a factory for even basic stuff like this, then I will call you a psychopath.

    • @e.a.p
      @e.a.p Месяц назад +1

      @@mk72v2oq oh shut up

    • @MrHirenP
      @MrHirenP Месяц назад

      Lmao😂

  • @RicardoCapurro
    @RicardoCapurro Месяц назад +1

    Treats c++ features as bugs, do not understand very well how it works or how RAII work!

  • @lekalotte2825
    @lekalotte2825 Месяц назад +1

    You should have shown modern C++ with default initializers. Showing a coding style from 20 years ago is lame.

  • @GiovanniCKC
    @GiovanniCKC Месяц назад +1

    2:20 but why? *why* does that make it call the wrong method? I need to knoooooooowwww

    • @anon_y_mousse
      @anon_y_mousse Месяц назад +1

      Because the object isn't fully constructed yet and virtual functions are part of the hidden abstractions of construction. Basically, you can expect that any virtual function won't be fully "constructed" until after the end of the constructor function call. The reason is because the virtual functions can be called at any time by the owner of the object after it has been constructed. This allows for objects to be fully constructed before results of that construction are used.
      Consider also that the super object will be constructed first, and all of its implemented virtual functions will be available then. After that point, the sub object which is inheriting from it will be constructed and its virtual table will be fixed up after it has been fully constructed.

    • @GiovanniCKC
      @GiovanniCKC Месяц назад +1

      @@anon_y_mousse thank you for the thorough and clear explanation :)

  • @readwrite-p5g
    @readwrite-p5g Месяц назад

    I use this best practice of rust

  • @Bankoru
    @Bankoru Месяц назад

    ok but you can also do all that in C#

  • @armanradan
    @armanradan Месяц назад +12

    Rust is extremely well thought and it's beautiful! It's a shame that most companies resist to use it more. They say it's too complicated and I agree some aspects of rust like lifetimes and pinning are difficult to deal with, but it's not like rocket science! And you can make it really easy using a little bit of AI help.

    • @NabekenProG87
      @NabekenProG87 Месяц назад +3

      Adding to this: I learned so much by slogging myself through the complicated parts. It taught me the pitfalls I fell into in other languages without even realizing it. I love C because it taught me how computers work, Rust had a similar role.

    • @tenspd137
      @tenspd137 Месяц назад

      Yeah, you like it, but guess what companies don't want to do - spend the money to replace their codebase that has worked for the last 20 or so years. That's why industry resists.

    • @skullbearer5067
      @skullbearer5067 Месяц назад +1

      It is also the case that there isn't enough of a pain point being addressed by Rust in a short enough period of return to justify the investment, plus there is the usual mental and emotional inertia to any change decision.

    • @armanradan
      @armanradan Месяц назад

      @@NabekenProG87 Exactly! I've learned so much about threading and shared references in all other languages by learning Rust. And it's not only the complicated parts which teaches us. It always suggests the best way to do things and I'm always amazed by it. I tried to spend more time on Go last month because companies want it more and pay really well for it. Then I learned that Go uses slices instead of vectors and lists. The problem is, when you want to remove an item from a slice, you have to either create a new slice using parts from the original one, or re-arrenge following items manually. Then I came back to rust and called Remove on a vector. It suggested to use swap_remove if order is not important. I tried it and it said this function replaces the reomved item with the last one in the collection. It's amazing that Rust unlike Go, has really high level abstractions (almost zero cost) but teaches us about implementation details and lets us use the best strategy based on our use case!

    • @armanradan
      @armanradan Месяц назад +1

      @@tenspd137 It doesn't need to replace existing codebase. We can use it in new projects and even microservices.

  • @pawewodarczyk1546
    @pawewodarczyk1546 Месяц назад +3

    From my perspective a lack of implicit, or worse undefined, field initialization is a good stuff. What is still missing in Rust are default and named function parameters. Especially a lack of named parameters can be messy when using factory functions for structures having multiple fields and when they are of a same type it's even worse. Kotlin is an example how I would like to see it implemented as a base. Although even there I would like to have additionally an enforced mode where specifying parameter names is always mandatory, not optional.

    • @ToanNguyen-ue8rw
      @ToanNguyen-ue8rw Месяц назад +1

      You may want to check out the bon crate

    • @Mankepanke
      @Mankepanke Месяц назад

      I was agreeing with you for the longest time, since before Rust 1.0, but now that I've seen bon I'm not so sure we need it anymore :)

  • @codeline9387
    @codeline9387 Месяц назад +1

    what about jobs?

  • @discreaminant
    @discreaminant Месяц назад +2

    C++ diffs Rust in terms of in place allocation
    In Rust, u can only rely on optimization, so good luck on the debug mode (stack overflow), or unsafe
    Rust could never

  • @pedrogaxiola9140
    @pedrogaxiola9140 Месяц назад

    Dang. I thought this was an R (stats) video 😅

  • @tiranito2834
    @tiranito2834 Месяц назад +1

    The things explained around 3:30, how are they any different from how you'd do it in C? I'm genuinly curious, I want to know if there's something else that makes it different.
    typedef struct {
    char *name;
    uint32_t age;
    } Person;
    int main()
    {
    Person person = {"Alice", 30};
    return 0;
    }
    If you want a custom initializer with extra logic, then create a function that returns a person, just like what the video says at around 3:40
    Person PersonNew(char const *name, int age)
    {
    // Custom initialization logic, validation, resource acquisition...
    Person ans = {name, age};
    return ans;
    }
    As you said, that's just a regular function, so what difference does it have from this?
    And if you want result types, then what's the difference with this?
    int PersonNew(Person *self, char const *name, int age)
    {
    if(age < 18)
    return 1;
    self->name = name;
    self->age = age;
    return 0;
    }
    int main()
    {
    Person person;
    int success = PersonNew(&person, "Alice", 30);
    if(success != 0)
    {
    printf("Error : %s
    ", GetErrorStringPersonNew(success));
    return 1;
    }
    return 0;
    }
    And if you don't like the C style of using an integer status and translating it to a static error string, you can always do it in a more Rust-like style with return structs that contain a value and an error status.

    • @furno_2761
      @furno_2761 Месяц назад

      Afaik the only difference is that in rust you have to specify the values of all fields.

    • @tiranito2834
      @tiranito2834 Месяц назад

      @@furno_2761 I guess you mean when using the brace initialization in C, no? True, you can indeed not initialize all values, but if you use a function you can still enforce initializing all of the fields. In any case, for any complex struct with advanced functionality it would make sense to make use of explicit functions for "construction" / initialization.
      Also if we're strict when thinking about the differences, I guess also the ownership of the pointers and stuff, but there is not a string struct in C, and functions called with dot notation technically compile to a function call that takes a "self" / "this" pointer as a parameter, and this holds true for C++ and Rust, so even then in this example it would not be a relevant difference, cause the functions are handling all of that logic internally so it's literally just the same as in Rust.

  • @torarinvik4920
    @torarinvik4920 Месяц назад +1

    I am one of the few that disagree with the composition over inheritance slogan. I follow the rule 1. "Is a" means inheritance or interface. 2. "Has a" means composition. 3. "Uses a" means delegation. I rarely do use inheritance though, as I usually use algebraic data types with pattern matching instead. This gives me a more flexible form of polymorphism, where I can do multiple dispatch and more. It's incorrect to say that an Employee has a Person, that doesn't make sense. In this case I would rather just use good ol' fashion code duplication.

    • @jcm2606
      @jcm2606 Месяц назад

      At least IMO, when dealing with data inheritance (ie you're inheriting some data from an abstract type), "has a" is virtually equivalent to "is a" if you have an adequately abstract public API. If you have some object `A` that has an instance of some object `B` stored inside, and `A` has a number of methods that silently accesses things from the inner instance of `B`, does it really matter to an outside observer if `A` is an instance of `B`?

    • @torarinvik4920
      @torarinvik4920 Месяц назад

      @@jcm2606 To be honest I don't quite understand what you mean. I do agree that inheritance is fragile though and that interfaces should be used unless you have a good reason to use inheritance over it.

  • @HosseinOjvar
    @HosseinOjvar Месяц назад

    Rust is GOD

  • @hermes6910
    @hermes6910 Месяц назад +1

    1:25
    Not the best example, just use uint, even better uint8_t ?

    • @yondaime500
      @yondaime500 Месяц назад

      The C++ Core Guidelines say "No, using unsigned is not a good way to sidestep the problem of ensuring that a value is non-negative."

    • @hermes6910
      @hermes6910 Месяц назад

      @@yondaime500 because of possible problems at runtime ?

  • @Maskrade
    @Maskrade Месяц назад +1

    factories:

  • @stevenspungin6704
    @stevenspungin6704 Месяц назад

    Traditional constructors also cannot be async, wheras Rust constructors can be.

  • @hiddenau
    @hiddenau Месяц назад +1

    While I like the channel, I wished less time is spent comparing Rust to other languages. The creator of the channel is obviously not a C++ expert and thus not able to provide nuanced examples. Instead I much rather more time is devoted to Rust education.

  • @BartKus
    @BartKus Месяц назад +1

    Honestly this is all doable in C++ with a couple compiler flags and clang-tidy calls. If you wanna stick to using factory functions instead of constructors you're free to do so as well. But give it 10 years and the explicit verboseness gets tiresome, and you'll reinvent constructors and also call it some new wonder of programming language design. I'm too old for all this, get off my lawn.

  • @jmsdnns
    @jmsdnns Месяц назад +1

    I wanna have like 500 of Rust's babies. I think I'll use this pattern when each baby is born.

  • @lesliejohnrichardson
    @lesliejohnrichardson Месяц назад

    18 Minutes ago is NOT crazy

  • @spik330
    @spik330 Месяц назад +1

    2:11 now that just sound like a bug in the way C++ handles method initialization, as this is undefined behavior.

    • @dshindov
      @dshindov Месяц назад +1

      I couldn't reproduce this example tbh

  • @azharalibhutto1209
    @azharalibhutto1209 Месяц назад

    ❤❤❤❤❤❤❤❤❤❤❤

  • @fischi9129
    @fischi9129 Месяц назад +2

    Idk, half of this video sounded and there is a pitfall if you don't know about it... well yeah, you should know the lang you are using ngl...

  • @floppypaste
    @floppypaste Месяц назад

    Althoug i absolutly agree with the overall message of the video, there is one thing i disagree with:
    Rusts Result type is actually not that different to plain old exception handling (but more consistent, as it is part of the language), even in your example the caller needs to know which errors can be trown to handle them accordingly.

    • @Mankepanke
      @Mankepanke Месяц назад

      Checked errors is one of the things that make it good, though. Not a downside, but the point of it.

    • @jcm2606
      @jcm2606 Месяц назад

      I'd more so say that it's not that different to checked exceptions, since plain old exceptions can be thrown anywhere and you won't know until your program either crashes randomly, or the exception is caught in some obscure try/catch statement somewhere down the call stack. `Result` and checked exceptions both at least tell you that some code can possibly produce an error, and they both force you to handle that error. The main advantage of `Result` over checked exceptions is that it's part of the type system and so naturally benefits from Rust's rich type system.

  • @anon_y_mousse
    @anon_y_mousse Месяц назад +1

    Rust constructors are one of its biggest flaws. It's basically forcing you to use Java in a C-ish way. What's worse, is that because the constructor can be renamed to whatever the developer wishes, it even provides an extra avenue of guesswork. This means that either everyone uses one naming convention and then why should you have to name it at all, or it forces you to look it up in the documentation/code. All of the extra garbage that you have to add such as .to_owned() and .into() and either using .unwrap() or match and a clunky switch-esque syntax just adds to my list of annoyances. Not that I would expect a brainwashed zealot to understand this complaint, and you've certainly never responded to my messages so why start now, but easy things should be easy and hard things should be hard yet Rust and far too many new languages like to make the mistake of inverting responsibility. It really screws up the mind of a newbie if they learn with one of these backwards languages because they don't understand the fundamentals of how anything works.

    • @RustIsWinning
      @RustIsWinning Месяц назад

      Holee the amount of copium tanks again LMAO 🤣

    • @anon_y_mousse
      @anon_y_mousse Месяц назад

      @@RustIsWinning I know, you don't understand how things work, it's okay.

    • @RustIsWinning
      @RustIsWinning Месяц назад

      @@anon_y_mousse My guy can you even remember the 3 letter word "new"? LOL! Maybe a bit C-nile if it is so hard to know simple conventions ROFL. Write as much garbonzo as you want like the other 33 comments here. Rust is living rent free in your head hahaha

    • @RustIsWinning
      @RustIsWinning 28 дней назад

      @@anon_y_mousse What do I not understand? The fact that there are people who complain about a 3 letter naming convention because the language was designed that way? Okay buddy if that's the problem maybe go see a doctor LMAO

    • @anon_y_mousse
      @anon_y_mousse 28 дней назад

      @@RustIsWinning What you're saying makes no sense and doesn't apply to what I've said here.

  • @williamdroz6890
    @williamdroz6890 Месяц назад +1

    IMO C++20 concepts are the only thing that C++ is doing better than Rust.

    • @T1Oracle
      @T1Oracle Месяц назад +2

      Unfortunately, all of those legacy features that should have been discontinued decades ago, are still part of the language. So you still have to deal with the possibility of someone using them. Which is tough, because there are so many features. Even with decades of experience, you will still not be aware of every feature, and every syntax trick, in C++. Then you have macros, templates, and a plethora of memory and thread safety issues.

    • @tenspd137
      @tenspd137 Месяц назад

      And guess what - you are going to get just as many problems, just different ones, with rust.

  • @Cathyprime
    @Cathyprime Месяц назад +5

    Literally you can make a private constructor and have static methods to create the object, as a mater of fact you can do shit like that in any language supporting methods for classes... this isn't some great rust feature...

    • @ThisIsNotAUsername-v3o
      @ThisIsNotAUsername-v3o Месяц назад +1

      Rust does make it easier to notice, since the constructor factory pattern is explicit. As a preference note, out of C#, C++, and Python, I do prefer Rust.

    • @EpKjelltzer
      @EpKjelltzer Месяц назад +1

      The great Rust feature is that it doesn't support the other ways, because they're known to cause issues. The feature is not "you can construct and return an object via a function", it's "there's no such thing as a constructor, aka a special function with lots of pitfalls".

    • @ThisIsNotAUsername-v3o
      @ThisIsNotAUsername-v3o Месяц назад

      ​@@EpKjelltzer That is true; I didn't address that part because it seemed tangential to the point under discussion.
      That being said also, having no software-defined convention for constructing an instance of a type is also a potential pitfall. The perfect programming language is a verb, not a noun.

    • @Cathyprime
      @Cathyprime Месяц назад

      @@EpKjelltzer MyClass() = delete;

  • @olafbaeyens8955
    @olafbaeyens8955 Месяц назад +1

    One thing I did back in C++ and C# is to make sure that nothing in a constructor can fail. A constructor has 100% guaranteed initialization.
    I have a second method Init() that can fail and gives an indication that the code is doing something more than just initializing variables.
    This Init() can also be reused later in my code to reset the initial state, so I don't need another constructor and memory allocation. Just reset it and I do not have to update pointers everywhere.
    When I create the Init(), I also create the Reset(), which calls the Init() internally, so developers using my code have a clear description of what it does.
    You see something like:
    var myClass = new myClass();
    myClass.Init();
    Variation could also be
    var myClass = new myClass(autoInit= true);

    • @NotherPleb
      @NotherPleb Месяц назад +3

      In c++, 2 step initialization goes against the their guidelines, they actually recommend throwing exceptions

    • @hoyoreverse
      @hoyoreverse Месяц назад

      But that allows you to have broken (not fully initialized) state. And it's obviously not "if it compiles, it works" then cuz you may forgot to call init() and there's no compile-time check for that

    • @ClimateChangeDoesntBargain
      @ClimateChangeDoesntBargain Месяц назад

      the problem with inits is that you can forget to call init and have a object in a partially constructed state.

    • @olafbaeyens8955
      @olafbaeyens8955 Месяц назад

      @@NotherPleb Exceptions cause great misery in the end, because they are like GOTO's, you have no idea where it ends up. Your catch blocks become huge blobs of code, bigger than your functional code. Giant catch blobs make you hide the real functionality that is called 99.9999% of the time.

    • @olafbaeyens8955
      @olafbaeyens8955 Месяц назад

      @@ClimateChangeDoesntBargain But still safe to use because it is a predictable state.With an exception it jumps back to a random place and also have no meaningful information that helps a user.

  • @bothorsen4292
    @bothorsen4292 Месяц назад +2

    The constant C++ bashing on this channel is getting old very fast

    • @Greeem
      @Greeem Месяц назад +1

      I kinda agree. I liked this content at first and now it just feels like rust worship. All of the "issues" defined in this video are PEBKAC.

    • @EliteCPR
      @EliteCPR Месяц назад

      @@Greeem nah, footguns are bad design and should be treated like it

    • @RustIsWinning
      @RustIsWinning Месяц назад

      The only thing I see here that is getting old very fast are the dinosaurs that cope. Dont forget to this one boomer --> ♿️

    • @bothorsen4292
      @bothorsen4292 Месяц назад

      @@RustIsWinning wow, you really are clueless

    • @RustIsWinning
      @RustIsWinning Месяц назад

      @@bothorsen4292 Clueless about what? Alright wrong generation I guess. But still take these to cope: ♿️💊

  • @kleinmarb4362
    @kleinmarb4362 Месяц назад +1

    4:35 syntax error in the second line of Person::new

    • @buycraft911miner2
      @buycraft911miner2 Месяц назад +1

      isnt "Ok Self... " meant to be "Ok(Self...)"​?@@Traversed-g8h

  • @clot27
    @clot27 Месяц назад +2

    Rust hype

  • @KhaledKimboo4
    @KhaledKimboo4 Месяц назад +4

    This is a freaking religion, you guys are overly hyped over a language that ENFORCEs someone's idea of a good constructor onto you, just get better with any language you use. Hyped like It's God have sent the holy grail of constructors.
    is see rust suitable for programmers with "tell me what/how to do" mindset

    • @antifa_communist
      @antifa_communist Месяц назад +1

      It's better though.

    • @antifa_communist
      @antifa_communist Месяц назад +2

      It forces you to so something correctly. How is it bad to stop you from doing something wrong? Why would I want my fields to be possibly uninitiated? Have you even tried Rust?

    • @pierreollivier1
      @pierreollivier1 Месяц назад +2

      I think the main benefit of explicitness is to prevent insidious bugs. Explicitness is good, languages that try to hide every single low level details and focuses on making it easy to write, are optimizing for the wrong metric, C++ constructor are just poorly designed, like most of the language is, I'm saying that as someone dealing with C++ on a daily basis, it's such a bad language, fortunately enough I'm able to rewrite most of it.

    • @jcm2606
      @jcm2606 Месяц назад +1

      The funny thing is that your argument applies much more strongly to C++ and other languages with constructors than Rust, because Rust doesn't even have constructors. What Rust has is associated/static functions, which are just plain old functions that just so happen to belong to the struct's namespace, making it convenient to relate the function with the type. A Rust "constructor" is literally just a plain old function that wraps the primitive construction of the struct, so that "someone" is actually _you,_ since _you_ are the one who dictates how _your_ "constructor" works, the language just gives you an associated function as a starting point.

    • @pierreollivier1
      @pierreollivier1 Месяц назад

      @@jcm2606 Exactly, that's the same has having an init function in C where you pass a pointer to your struct, or arguments and get an initialized struct of your type, nothing fancy, just good old function, without 30 overloading, simple, easy to reason about, effective.

  • @sunofabeach9424
    @sunofabeach9424 Месяц назад +1

    isn't this achievable in like any other language? just uhh define it explicitly and make constructor private

    • @jackkendall6420
      @jackkendall6420 Месяц назад +3

      What rust gives you is the *guarantee* objects will always be fully initialised. You don't have to rely on everyone else making their ctors private and giving you factory functions.

    • @sunofabeach9424
      @sunofabeach9424 Месяц назад +1

      @@jackkendall6420 it will be initialized fully but not always correctly. that's why you will most likely create a 'new' function, basically, returning to constructors. so it basically boils down to patterns: in Rust you need to define constructors, in OO languages - make them private. and while the former approach is more clean, there is nothing 'genius' to it

    • @diadetediotedio6918
      @diadetediotedio6918 Месяц назад

      No, it is not. In C# for example you can have null values and structs can be 'default' initialized, so making their constructors private don't ensure correctness. In Rust you to initialize things.

    • @CjqNslXUcM
      @CjqNslXUcM Месяц назад +2

      ​@@sunofabeach9424 Yeah, it's rather banal. But you'll appreciate it when you work with other people's code.

    • @sunofabeach9424
      @sunofabeach9424 Месяц назад

      @@diadetediotedio6918 nulls and constructors are unrelated to each other. you can have a language without nulls but with constructors

  • @markg5891
    @markg5891 Месяц назад +1

    Ohh definitely not! Rust and simplicity do not go hand in hand!
    Rust just has a different syntax that "resembles" c/c++/java but is still way different! It's generally agreed to be a more complex syntax compared to c/c++/java.
    The C++ example given is true but calling it an "unintentional bug" is stretching it, i'd call it a bad developer. Sure, these are some of the more intricate C++ details you need to get right when developing in C++. Like rust has weirdness (borrow... anyone), C++ has other weirdness. Rust avoids pitfalls, sure, but introduces many new of it's own.
    Rust is still an amazing language! :)

    • @letsgetrusty
      @letsgetrusty  Месяц назад

      Agreed, every langauge has rough spots and weridness. I'm curious to know what pitfalls Rust has in your opinion

    • @EpKjelltzer
      @EpKjelltzer Месяц назад +1

      I don't think you can say that Rust's syntax is more complex than Java or especially C++. Definitely there's an argument to be made about semantics (although nothing is as complex as C++'s semantics), but Rust's syntax is really not complex. Perhaps unfamiliar, but not complex.
      The big difference is that Rust's "pitfalls", at least based on the most common complaint's I've seen, are compiler errors that people have a hard time understanding. And that's definitely complex, but in a completely different way than the C++ pitfalls that seem to work until your program blows up in a way that is extremely difficult to trace to the original bug. You would call the developer bad rather than blame the language when the C++ bug is encountered, and yet we now have many decades of proof that there's no such thing as a "good" developer if we assume that to mean someone who doesn't introduce subtle bugs. So the only clear way forward is to expect more from our tools. I would call it a failure on C++'s part to allow developers to write clearly bad code.

    • @NotVersace
      @NotVersace Месяц назад +2

      "bad developer" causes "unintentional bug". That's the point. By avoiding pitfalls as part of the language design you have less unintentional bugs as a result of bad developers.

    • @markg5891
      @markg5891 Месяц назад

      @@EpKjelltzer Some nuance on the "bad developer" part. I said that with the assumption of developing in C++ is using it's inheritance model. These inheritance pitfalls (and there are many) are just the learning curve of using C++ with inheritance. A good developer (where good means someone who knows how to write C++ and actually learned the language over years) either instinctively knows these cheap-shot pitfalls and thus prevents them or knows how to run the code to detect falling for the same trap and fixes it.
      A developer that knows other languages and just takes a dab into C++ (without learning it, just applying logic from different languages to rust and assuming it to work the same) might well be able to build something that compiles and runs. But will also fall for the pitfalls and then have a hard time finding out why it happens. It might still be a great dev!
      I agree with you that tooling can help massively here!

    • @pierreollivier1
      @pierreollivier1 Месяц назад +1

      There come a point where you have to consider that if everyone is having "skill issues" with your language, than maybe your language is the problem. I don't see why companies like Google, Apple, Microsoft, would go out of their way and spend so much on running away from C++ by developing their own languages (Swift, Go, Carbon) or using newer one (Rust) if C++ was so great, If your language needs 2300 pages of ISO specs just to defines itself when C barely scratches the 800 marks, I think we just need to call it a day and move on from it. C++ is just too complex, too bloated, poorly designed. I'm glad newer languages are coming to fix this mess.

  • @abcq1
    @abcq1 Месяц назад +3

    There's nothing wrong with C++ constructors. As you fairly said, when someone "doesn't know nuances" (which translates to just not knowing enough of the language and being immatiure programmer), he writes shit. Know your rlanguage. After which there are no problems.

    • @pierreollivier1
      @pierreollivier1 Месяц назад +5

      There come a point where if too many people have a "skill issue" with you language, your language as design issues. C++ is overly complex for no benefit, there never was a point in my life where I couldn't have generated the same assembly in C, Zig, Rust or any other compiled language. I'm not saying the language is completely useless, but the way people can't recognize how flawed the language is, is quite ridiculous, think about it if one can generate the same assembly, in a safer way, while being more readable and easier to maintain, with less pitfalls, then what is the actual proposition value of C++, what are the benefits of using C++ over any other LLVM powered language ? I just don't get why people use a low level language, to then hide every single low level detail behind walls of abstractions. The only good C++ I know is the one where you write C code, and use a few containers where it make sense.

    • @antifa_communist
      @antifa_communist Месяц назад +2

      Even experienced programmers get this wrong though. It's objectively bad. You said yourself you have to know all these nuances.

  • @4lxprime730
    @4lxprime730 Месяц назад +1

    rust is overrated right now, peoples must look at more balanced languages such as golang or zig

    • @knnk4000
      @knnk4000 Месяц назад +1

      sorry, not a gomonkey or a zigay :)

    • @RustIsWinning
      @RustIsWinning Месяц назад

      We found the gobesse zigooner lmao

    • @4lxprime730
      @4lxprime730 Месяц назад

      @@RustIsWinning that's funny, keep building your software with overcomplicated languages, i prefer the simplicity

    • @RustIsWinning
      @RustIsWinning Месяц назад +1

      @@4lxprime730 The real funny thing here is that go is slower and zig is not even memory safe haha

    • @4lxprime730
      @4lxprime730 Месяц назад

      ​@@RustIsWinning go is way more efficient to write than most languages (including rust btw) and is not meant to be as fast as c, just to get things done. zig is as fast as rust (can be faster with its allocator system and can be slower) and have all that you may want from a modern language including some safety. The problem with the rust hype is that you are only talking about safety which is a bit delusionnal cause safety is a human problem, you can do code as safe as rust's in c with 10x less complexity

  • @andreaselfving2787
    @andreaselfving2787 Месяц назад

    I still think go is a better option for developers coming from any other high-level languages, such as python etc

    • @frydac
      @frydac Месяц назад

      depends on your definition of better. There is no one that is best in all contexts, the answer to almost any question is 'it depends' (google kevlin henney - it depends )

  • @korniychuk
    @korniychuk Месяц назад

    Test for a "good programmer" 😃 If a programmer appreciates Rust's principles, strictness, and limitations, they are a good programmer. Writing code in other languages, such a programmer tends to write strict code, keeping Rust's limitations and principles in mind, even though the other language is less strict and more error-prone. A "bad programmer" dislikes all of Rust's reliability principles and limitations and prefers not to write code in Rust. Instead, they write fragile code without any safeguards in less strict languages and think "it's easier and faster, what's the problem?!". 🫠