1. 17
  1.  

  2. 2

    This articles makes a very good point, with the state of the class/object being in an inconsistent state in the constructor.

    I’ve been bitten in the arse by this. Was my humble C++ beginnings. In the constructor, I called some initialisation methods and set some variables. Turns out that one of the instance vars was used by a method before I set it, and that was causing sporadic crashes (depending on what the value happened to be before initialisation). That was such a pain to debug.

    1. 2

      As I keep saying, “Constructor” was a very bad choice of name.

      They are “Name Binders”.

      They bind their parameters to instance variable names.

      With that view unit testing and dependency injection suddenly becomes easy.

      1. 1

        If all they ever do is bind names, then they don’t need access to the entire language. Taking Rust as the obvious example of an (arguable) OO language with no constructors.

        // Declares a "tuple struct" with two items in it.
        // There are two differences between tuple structs and regular structs with named fields.
        // One of these differences is that the fields in tuple structs are nameless.
        struct MyTupleStruct(u32, &'static str);
        // The other difference is that tuple structs have "constructors" which are, literally, functions.
        // Here, I take a function pointer to the `MyTupleStruct` constructor.
        let my_constructor: fn(u32, &'static str) -> MyTupleStruct = MyTupleStruct;
        // And I call the function through the function pointer.
        let my_object = my_constructor(5, "test");
        // And here's the example.
        println!("{}", my_object.1);
        

        Try it https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e40f1f332a8f0d6bc8e7b7085ef90029

      2. 1

        I recommend reading it, it’s a great summary of the issues involved!

        Two notes:


        Constructor’s Signature is a great description of an issue I have also struggled with in my “own” language:

        I want to have declarations like class Person(let name: String, let age: Int).

        How to call it? There are two alternatives, and I both dislike them:

        1. Use Person("Joe", 42).
        • Good: Defining a constructor looks very similar to invoking it.
        • Bad: The call has to succeed, because you cannot return Option[Person] or Result[Person], only Person.
        • Bad: Factory functions get “worse” syntax, because the nice syntax, Person(...) is already gone.
        1. Use Person.new("Joe", 42).
        • Bad: Defining a constructor looks different to invoking it.
        • Good: Factory functions get use the “good” syntax, Person(...).
        • Good(?): With Person(...) being a call to a factory function, it can return Option[Person], Result[Person] or Person as required.

        There is one tiny piece from the article I disagree with:

        One can even imagine a language extension that allows one to mark certain functions with a #[constructor] attribute, with the effect that the record literal syntax is available only in the marked functions.

        I’d rather go the other way: Drop record literal syntax altogether.

        The syntax isn’t pulling its weight and it’s a completely unnecessary wrinkle in the language.

        Plus, it leads to this annoying stutter when writing factory functions:

        -> Foo { Foo {

        I have lost track of the times where I forgot the second Foo { and was wondering why the compiler was unhappy with my code.

        https://matklad.github.io//2019/07/16/perils-of-constructors.html#constructors-signature