<p>An object is partially initialized if the program execution didn't exit their constructor yet. Usually the programmer is responsible for not leaving partially initialized objects in unwanted places and calling them in unwanted ways. But like Rust has &quot;ownership&quot; and &quot;lifetime&quot; to protect against invalid pointer accesses, are there ways to protect against misusing partially initialized objects, while still allowing every operation that does useful work in the constructor?</p>
<p>My knowledge about Rust is limited, but if there aren't some edge cases that I don't know, it doesn't seem to have accessible partially initialized objects at all. But I'm thinking about languages with more traditional constructors like in C++.</p>
<p>The background of this question is, I'm thinking about how much damage could be done, if there could be async constructors, and objects are allocated in-place instead of referenced from elsewhere, which could leave a lot of partially initialized variables in random places.</p>
<p>Example C++ code that referred to an uninitialized object in this way:</p>
<pre class="lang-cpp prettyprint-override"><code>#include&lt;iostream&gt;

struct D {
    int x;
    D(){x=12345;}
};

struct A {
    int y;
    A();
};

struct B:A {
    D d;
    int dx(){return d.x;}
};

B b;

A::A(){std::cout&lt;&lt;b.dx()&lt;&lt;std::endl;}

int main(){}
</code></pre>

<constructors><type-safety>
What are the approaches of protecting against partially initialized objects?
CC BY-SA 4.0
2024-06-12T13:58:45.340Z
[Related](https://langdev.stackexchange.com/questions/2030/why-do-languages-differ-about-when-super-must-be-called-in-a-constructor), I think, but not quite the same
Bbrk24
CC BY-SA 4.0
2024-06-12T15:44:26.153Z
https://shipilev.net/blog/2014/safe-public-construction/
Moonchild
CC BY-SA 4.0
2024-06-12T20:22:19.823Z
Two interesting places this crops up in C#: (1) The spec says that C# initializes structs in temporary storage and copies to the destination when the constructor is complete, but the compiler will elide the copy if doing so is "safe". The safety property it is looking for is "could code outside the object observe partially constructed state if the constructor throws"?
Eric Lippert
CC BY-SA 4.0
2024-06-12T20:23:10.147Z
(2) Finalizable objects are eligible for finalization the moment they are created; it is possible in rare situations for the finalizer of a dead object to start running *before* the constructor finishes! Finalizers must be robust in the face of handling objects that were not successfully constructed.
Eric Lippert
<p>There are two related issues here: one is memory safety, and the other is domain invariants. We want to guarantee fields are initialised, because an uninitialised field with whatever bit pattern was previously left in that field's location might not be valid for the field's type (e.g. it may be a number out of range, a pointer to an object of the wrong type, or a stale pointer to an object which no longer exists). Additionally, we may also want programmers to be able to ensure their own invariants (e.g. for a <code>Range</code> type, that <code>this.start &lt;= this.end</code>).</p>
<h2>Definite assignment</h2>
<p>By static analysis, a compiler can determine that a field is or isn't &quot;definitely assigned&quot; at each point in the constructor. The goal is to check that all fields are definitely assigned before a reference to the object can escape (by any means).</p>
<p>This may be done via syntax-directed rules, <a href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-16.html" rel="noreferrer">as Java does</a> (albeit not for fields): for example, a field <code>x</code> is definitely assigned after a statement <code>this.x = ...;</code>, a field is definitely assigned after <code>if(...) A else B</code> if it is definitely assigned after <code>A</code> and definitely assigned after <code>B</code>, and so on. Alternatively, a <a href="https://en.wikipedia.org/wiki/Control-flow_graph" rel="noreferrer">control-flow graph</a> can be derived, then a variable is &quot;definitely assigned&quot; if every exit node is dominated by an assignment to that variable.</p>
<p>Such rules are conservative, so it is possible to write programs which in reality always assign to a variable, but which nonetheless don't satisfy the rules. The compiler will reject such programs; this could upset programmers who &quot;know better than the compiler&quot;. Typescript offers an escape-hatch via <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions" rel="noreferrer">definite assignment assertions</a>, allowing the programmer to override the compiler's judgement, although (like other escape hatches) this means opting out of having the compiler check the correctness of your code.</p>
<p>Definite assignment checks are based on the principle of proving that the fields will be assigned after some statement or set of statements complete normally. This means that separate guarantees need to be made for when the constructor does not complete normally (e.g. if it throws an exception, yields, or falls into an infinite loop). In those cases we want to ensure that a reference to the uninitialised object cannot be acquired. The rules to ensure this can often be more onerous than the more straightforward rules requiring definite assignment before the constructor terminates.</p>
<p>Normally, a reference to a partially initialised object can't escape from the code which invokes the constructor, since it receives no such reference in case the constructor fails (or, if the constructor is merely paused, the invoking code doesn't receive a reference to the new object until the constructor resumes and completes). However, the constructor itself must also not provide other code with a reference to <code>this</code> before all fields are initialised; in particular, instance methods should not be called, nor should a superclass constructor. (Alternatively, if a superclass constructor may be called earlier, then instance methods must not be called from anywhere in the superclass constructor, even after all superclass fields are definitely assigned.) If you allow constructors to yield, then likewise a constructor must not <code>yield this</code> (or similar) before all fields are initialised.</p>
<h2>Default field values</h2>
<p>In Java, fields have default initial values based on their types, such that a field has a well-defined value even if is not assigned in the constructor (or an initialiser). This is a semantic guarantee rather than a runtime requirement, so a compiler is permitted to optimise away the assignment of the default value if it is a <a href="https://en.wikipedia.org/wiki/Dead_store" rel="noreferrer">dead store</a>.</p>
<p>This means that objects in Java always have their fields initialised, guaranteeing memory safety and preventing implementation-specific behaviour, though it is not guaranteed that a default initial value for a field will satisfy any invariant intended by the programmer.</p>
<p>Note that this is only possible in Java because <code>null</code> is a valid value for all reference types. In general, non-nullable reference types do not have sensible default values, nor do function types, file handles, or so on.</p>
<h2>Allow fallible constructors</h2>
<p>In languages with exceptions, constructors can be permitted to throw them. This allows programmers to throw exceptions rather than allow objects to be created which violate their invariants. However, it doesn't help with memory safety since it depends on the programmer writing code to verify the object's state themselves.</p>
<h2>Ban fallible constructors</h2>
<p>A different approach is taken by languages like Rust, in which there are no non-trivial constructors or initialisers at all. Objects (really, structs and tuples) are created by directly providing values for all of their fields, and so it is trivially not possible to &quot;see&quot; an object before those values are assigned.</p>
<p>This guarantees memory safety, but means invariants (and other considerations such as encapsulation) must be addressed in a different way. Rather than writing a constructor which checks whether the object is being constructed correctly and throws an exception otherwise, programmers are expected to write factory functions which perform those checks before creating the object.</p>

CC BY-SA 4.0
2024-06-12T14:23:18.920Z
2024-06-13T15:06:40.307Z
2024-06-13T15:06:40.307Z
kaya3
CC BY-SA 4.0
2024-06-12T14:42:48.843Z
Most of this answer is about how to make a constructor work correctly and initialize every field. But I'm asking about a paused constructor, by calling other code within the constructor or using async constructors, assuming constructors always work correctly if they could be finished.
user23013
CC BY-SA 4.0
2024-06-12T14:52:32.213Z
@user23013 All of these approaches apply to the case of constructors being interrupted via `yield` or `await`, if your language allows constructors to be coroutines or asynchronous. A constructor which has not finished executing because it threw an exception, vs. a constructor which has not finished executing but might be resumed later, doesn't make much difference as far as I can tell. In approach 1 that means you can't `yield this` (or yield anything from which `this` is accessible), in approach 2 the default values prevent unsafety including while the constructor is paused, and so on.
kaya3
CC BY-SA 4.0
2024-06-13T07:14:01.450Z
Note that in Rust you can _deinitialize_ fields, leaving an object in a partially initialized state. The Rust compiler uses control-flow analysis to track each field's state and ensure all fields are properly initialized before a value is used, or alternatively to drop only the still initialized fields when the value is dropped.
Matthieu M.
CC BY-SA 4.0
2024-06-13T22:27:30.647Z
And the easiest way to implement "Default field values" is to memset the object's memory to 0.
dan04
CC BY-SA 4.0
2024-06-13T22:44:20.497Z
@dan04 That works for Java because of `null`, but if e.g. the default value for a list in your language should be an empty list, then constructing a default value might require another allocation. And if users are allowed to define default values for their own types (e.g. as Rust's `Default` trait allows) then it's more complicated.
kaya3
<p>The general problem you describe is to allow computations on constructors, and at the same time to avoid partially constructed objects that do &quot;escape&quot; into other contexts. This is something that linear type theory may help.</p>
<h2>Two stage construction constructor</h2>
<p>Inside the constructor, the object has a special type that cannot be expressed in code. And a syntax to mark the completion of the constructor, to allow a normal object to interact with the system, even inside the constructor.</p>
<pre class="lang-java prettyprint-override"><code>class C {
    def constructor( self, args... )
    {
        // self starts as an &quot;unborn&quot; object.
        // It's already allocated, but no invariants hold.

        // Also, self.c cannot be null and also cannot
        // mutate after construction (init only).

        self.a = ...;
        self.b = ...;

        MyCache.Add(self); // Compiler error: Unborn object cannot escape.
        self = new;        // Compiler error: Unassigned property.

        self.c = ...;

        // This completes the constructor, and guarantees all invariants
        self = new;

        MyCache.Add(self); // This is ok.
        self.c = ...;      // Compiler error, immutable read only property.
    }
}

var c = new C( args ... );
MyCache.Add(c);            // This is ok.
c.c = ...;                 // Compiler error, immutable read only property.
</code></pre>
<h2>Do not store references of partially constructed objects</h2>
<p>This may sound silly, but is a <a href="https://www.cs.umd.edu/%7Epugh/java/memoryModel/DoubleCheckedLocking.html#:%7E:text=store%20the%20unconstructed%20object%20here." rel="nofollow noreferrer">long standing gotcha</a> in Java and also in other languages, more close to metal. See the size of code for double-checked locking <a href="https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java" rel="nofollow noreferrer">examples in Wikipedia</a>!</p>
<p>The misconception is that:</p>
<pre><code>singletons[i].reference = new Singleton();
</code></pre>
<p>means:</p>
<ol>
<li>Allocate instance</li>
<li>Run constructor</li>
<li>Store object reference</li>
</ol>
<p>But in multi-threaded C/C++/Java this also means:</p>
<ol>
<li>Allocate instance</li>
<li>Store object reference</li>
<li>Run constructor</li>
</ol>
<p>So an <strong>another</strong> thread can access <code>singletons[i].reference</code> and get a reference to a non constructed object. This invalidates all invariants!</p>
<p>For example, any non-null object property can now return null values. How bad is that?</p>
<h2>Allow constructors to fail graciously</h2>
<p>Running computations on a constructor means that these computations can fail. But if the only way to communicate these failures is to throw exceptions, the code may end up cluttered with exception handling code, <em>even</em> these failures are expected.</p>
<p>A gentle way is to allow constructors to fail more graciously</p>
<pre class="lang-java prettyprint-override"><code>def constructor ( self? , args... )
{
    // Note the ? on self parameter.
    // This means that this constructor may not create an object.

    var memory = malloc( 1000 );
    if ( memory == null )
        return null;
}
</code></pre>
<p>And by so, code now can be more simple:</p>
<pre class="lang-java prettyprint-override"><code>var fileinfo = new FileInfo(path) ?? FileInfo.Empty;

// or

var fileinfo = new FileInfo(path); // fileinfo is FileInfo?
if ( fileInfo ) { ... }            // fileinfo is FileInfo inside if
else { ... }                       // Failed to obtain file fin
</code></pre>
<h2>Allow constructors to return errors</h2>
<p>Same as above, buy we are interested on <strong>why</strong> FileInfo constructor failed.</p>
<pre class="lang-java prettyprint-override"><code>def constructor ( args... ) self , error
{
    // Multiple returns on constructors are always disjoint unions
    // Alternative, explicit syntax:
    //
    //     def constructor () self |,| error

    if ( ... )
        return self;
    return (error) err;
}

var info, err = new FileInfo(path);

Console.Log( info.FileSize ); // Compiler error, disjoint type direct access.
throw err;                    // Compiler error, disjoint type direct access.

if ( info ) { ... } // Ok
else throw err;     // Ok
<span class="math-container">```</span>
</code></pre>

CC BY-SA 4.0
2024-06-12T16:15:52.617Z
2024-06-13T14:33:49.353Z
2024-06-13T14:33:49.353Z
André L F S Bacci
CC BY-SA 4.0
2024-06-12T16:36:59.580Z
Swift and Objective-C have something similar to the last one; In Swift it's spelled `init?(...)`, and in Objective-C it's done by marking the return type as `instancetype _Nullable` or `instancetype __nullable` rather than just `instancetype`.
Bbrk24
CC BY-SA 4.0
2024-06-14T15:49:42.820Z
"But in multi-threaded C/C++/Java this also means […]" – Even in Java? I find that incredibly surprising! My understanding of that statement would have been that the expression `new Singleton()` doesn't finish evaluating until the constructor has finished running, and the assignment doesn't occur until its right-hand side has finished evaluating. Which of these is not true for Java?
Sophie Swett
<p>The simplest method is to by default initialize to <em>something</em> as soon as the lifetime of an object begins.</p>
<p>In D this is the <code>Foo.init</code> value which every type has which gets memcopyd onto the variable when the lifetime begins. It can be customized in the definition of the struct. You can also just pick zero as initial value for everything.</p>
<p>Then you can opt-out by declaring the initial value of your variable as void: <code>Foo f = void;</code></p>
<p>Straight forward compiler optimizations will let dead stores (where a field gets written twice without being read inbetween) get optimized out in the common case.</p>
<p>Using this scheme means that the programmer will need to opt-into uninitialized values.</p>
<p>Designing a constructor around making an object immutable after construction can be done by making the constructor the exception to not being able to write to the non-mutable fields (the others are still default init where it matters). Or by delaying actually writing the fields until a designated point in the constructor. Then you have a preamble that computes the actual fields after which the fields get written and the lifetime officially begins, and a epilogue where it might fit itself into a datastructure as it might need.</p>

CC BY-SA 4.0
2024-06-12T13:59:02.383Z
2024-06-12T13:59:02.383Z
ratchet freak
<p>I'd like to add an interesting solution Bob Nystrom used in one of his languages, Magpie (explained in <a href="https://journal.stuffwithstuff.com/2010/12/14/the-trouble-with-constructors/" rel="noreferrer">this article</a>):</p>
<p>Basically, every class has a factory method, <code>new</code>, which by default simply creates an object using the given parameters and can be overridden to supply some more complex initialization logic. The trick is that within these methods, there's no way to actually access any instance variables of the new object, since it hasn't actually been created yet!</p>
<p>Instead, at the end of that method you call another, built-in method called <code>construct</code>, which takes in a record (basically a map) containing a key and value for every instance variable, and returns a new instance of the specific class initialized accordingly. That way, instance creation is <em>atomic</em>, meaning it is completely prohibited by design to perform operations on a <em>partially initialized</em> object.</p>

CC BY-SA 4.0
2024-06-12T19:45:08.270Z
2024-06-12T19:45:08.270Z
linux_user36
CC BY-SA 4.0
2024-06-12T20:26:43.013Z
Rust's approach, too, is to make the creation of structs an atomic operation. The rest of the details (that `construct` is a named function rather than using the name of the type, that `new` has a mandated name and a default implementation) are different, but the mechanism for preventing partial initialisation is the same.
kaya3
CC BY-SA 4.0
2024-06-13T05:58:42.680Z
@kaya3 True, it is functionally pretty much equivalent. Though the semantics is different (as you're already "inside the object" instead of creating it "from the outside") and it's a nice workaround for a dynamically typed language.
linux_user36
CC BY-SA 4.0
2024-06-13T14:09:45.413Z
I'd argue that you aren't "inside" the object because there is no `this` to use; the object is created by `construct`, so there is no object for most of `new` despite its name.
kaya3
CC BY-SA 4.0
2024-06-15T10:42:28.803Z
As mentioned by kaya3, you are not "inside" the object in Rust, because factory functions ("constructors") _do not take a `self`_ parameter. The only "constructors" baked into the language are `Type` (for types without fields), `Type(...)` (for types with unnamed fields), and `Type { ... }` (for types with named fields). And those are atomic.
Matthieu M.
# What are the approaches of protecting against partially initialized objects?
Posted by: username_0 on 2024-06-12
An object is partially initialized if the program execution didn't exit their constructor yet. Usually the programmer is responsible for not leaving partially initialized objects in unwanted places and calling them in unwanted ways. But like Rust has "ownership" and "lifetime" to protect against invalid pointer accesses, are there ways to protect against misusing partially initialized objects, while still allowing every operation that does useful work in the constructor?
My knowledge about Rust is limited, but if there aren't some edge cases that I don't know, it doesn't seem to have accessible partially initialized objects at all. But I'm thinking about languages with more traditional constructors like in C++.
The background of this question is, I'm thinking about how much damage could be done, if there could be async constructors, and objects are allocated in-place instead of referenced from elsewhere, which could leave a lot of partially initialized variables in random places.
Example C++ code that referred to an uninitialized object in this way:
#include

struct D {
    int x;
    D(){x=12345;}
};

struct A {
    int y;
    A();
};

struct B:A {
    D d;
    int dx(){return d.x;}
};

B b;

A::A(){std::cout<
**Tags:** constructors type-safety
> **Comments:**
> **username_1**: [Related](https://langdev.stackexchange.com/questions/2030/why-do-languages-differ-about-when-super-must-be-called-in-a-constructor), I think, but not quite the same  
> **username_2**: https://shipilev.net/blog/2014/safe-public-construction/  
> **username_3**: Two interesting places this crops up in C#: (1) The spec says that C# initializes structs in temporary storage and copies to the destination when the constructor is complete, but the compiler will elide the copy if doing so is "safe". The safety property it is looking for is "could code outside the object observe partially constructed state if the constructor throws"?  
> **username_3**: (2) Finalizable objects are eligible for finalization the moment they are created; it is possible in rare situations for the finalizer of a dead object to start running *before* the constructor finishes! Finalizers must be robust in the face of handling objects that were not successfully constructed.  
---
## 4 answers
### Answer by username_4 on 2024-06-12. Score: 9
There are two related issues here: one is memory safety, and the other is domain invariants. We want to guarantee fields are initialised, because an uninitialised field with whatever bit pattern was previously left in that field's location might not be valid for the field's type (e.g. it may be a number out of range, a pointer to an object of the wrong type, or a stale pointer to an object which no longer exists). Additionally, we may also want programmers to be able to ensure their own invariants (e.g. for a `Range` type, that `this.start <= this.end`).

## Definite assignment

By static analysis, a compiler can determine that a field is or isn't "definitely assigned" at each point in the constructor. The goal is to check that all fields are definitely assigned before a reference to the object can escape (by any means).

This may be done via syntax-directed rules, [as Java does](https://docs.oracle.com/javase/specs/jls/se21/html/jls-16.html) (albeit not for fields): for example, a field `x` is definitely assigned after a statement `this.x = ...;`, a field is definitely assigned after `if(...) A else B` if it is definitely assigned after `A` and definitely assigned after `B`, and so on. Alternatively, a [control-flow graph](https://en.wikipedia.org/wiki/Control-flow_graph) can be derived, then a variable is "definitely assigned" if every exit node is dominated by an assignment to that variable.

Such rules are conservative, so it is possible to write programs which in reality always assign to a variable, but which nonetheless don't satisfy the rules. The compiler will reject such programs; this could upset programmers who "know better than the compiler". Typescript offers an escape-hatch via [definite assignment assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions), allowing the programmer to override the compiler's judgement, although (like other escape hatches) this means opting out of having the compiler check the correctness of your code.

Definite assignment checks are based on the principle of proving that the fields will be assigned after some statement or set of statements complete normally. This means that separate guarantees need to be made for when the constructor does not complete normally (e.g. if it throws an exception, yields, or falls into an infinite loop). In those cases we want to ensure that a reference to the uninitialised object cannot be acquired. The rules to ensure this can often be more onerous than the more straightforward rules requiring definite assignment before the constructor terminates.

Normally, a reference to a partially initialised object can't escape from the code which invokes the constructor, since it receives no such reference in case the constructor fails (or, if the constructor is merely paused, the invoking code doesn't receive a reference to the new object until the constructor resumes and completes). However, the constructor itself must also not provide other code with a reference to `this` before all fields are initialised; in particular, instance methods should not be called, nor should a superclass constructor. (Alternatively, if a superclass constructor may be called earlier, then instance methods must not be called from anywhere in the superclass constructor, even after all superclass fields are definitely assigned.) If you allow constructors to yield, then likewise a constructor must not `yield this` (or similar) before all fields are initialised.

## Default field values

In Java, fields have default initial values based on their types, such that a field has a well-defined value even if is not assigned in the constructor (or an initialiser). This is a semantic guarantee rather than a runtime requirement, so a compiler is permitted to optimise away the assignment of the default value if it is a [dead store](https://en.wikipedia.org/wiki/Dead_store).

This means that objects in Java always have their fields initialised, guaranteeing memory safety and preventing implementation-specific behaviour, though it is not guaranteed that a default initial value for a field will satisfy any invariant intended by the programmer.

Note that this is only possible in Java because `null` is a valid value for all reference types. In general, non-nullable reference types do not have sensible default values, nor do function types, file handles, or so on.

## Allow fallible constructors

In languages with exceptions, constructors can be permitted to throw them. This allows programmers to throw exceptions rather than allow objects to be created which violate their invariants. However, it doesn't help with memory safety since it depends on the programmer writing code to verify the object's state themselves.

## Ban fallible constructors

A different approach is taken by languages like Rust, in which there are no non-trivial constructors or initialisers at all. Objects (really, structs and tuples) are created by directly providing values for all of their fields, and so it is trivially not possible to "see" an object before those values are assigned.

This guarantees memory safety, but means invariants (and other considerations such as encapsulation) must be addressed in a different way. Rather than writing a constructor which checks whether the object is being constructed correctly and throws an exception otherwise, programmers are expected to write factory functions which perform those checks before creating the object.
> **Comments:**
> **username_0** (2024-06-12): Most of this answer is about how to make a constructor work correctly and initialize every field. But I'm asking about a paused constructor, by calling other code within the constructor or using async constructors, assuming constructors always work correctly if they could be finished.  
> **username_4** (2024-06-12): @user23013 All of these approaches apply to the case of constructors being interrupted via `yield` or `await`, if your language allows constructors to be coroutines or asynchronous. A constructor which has not finished executing because it threw an exception, vs. a constructor which has not finished executing but might be resumed later, doesn't make much difference as far as I can tell. In approach 1 that means you can't `yield this` (or yield anything from which `this` is accessible), in approach 2 the default values prevent unsafety including while the constructor is paused, and so on.  
> **username_5** (2024-06-13): Note that in Rust you can _deinitialize_ fields, leaving an object in a partially initialized state. The Rust compiler uses control-flow analysis to track each field's state and ensure all fields are properly initialized before a value is used, or alternatively to drop only the still initialized fields when the value is dropped.  
> **username_6** (2024-06-13): And the easiest way to implement "Default field values" is to memset the object's memory to 0.  
> **username_4** (2024-06-13): @dan04 That works for Java because of `null`, but if e.g. the default value for a list in your language should be an empty list, then constructing a default value might require another allocation. And if users are allowed to define default values for their own types (e.g. as Rust's `Default` trait allows) then it's more complicated.  
---
### Answer by username_7 on 2024-06-12. Score: 6
The general problem you describe is to allow computations on constructors, and at the same time to avoid partially constructed objects that do "escape" into other contexts. This is something that linear type theory may help.
Two stage construction constructor
Inside the constructor, the object has a special type that cannot be expressed in code. And a syntax to mark the completion of the constructor, to allow a normal object to interact with the system, even inside the constructor.
class C {
    def constructor( self, args... )
    {
        // self starts as an "unborn" object.
        // It's already allocated, but no invariants hold.

        // Also, self.c cannot be null and also cannot
        // mutate after construction (init only).

        self.a = ...;
        self.b = ...;

        MyCache.Add(self); // Compiler error: Unborn object cannot escape.
        self = new;        // Compiler error: Unassigned property.

        self.c = ...;

        // This completes the constructor, and guarantees all invariants
        self = new;

        MyCache.Add(self); // This is ok.
        self.c = ...;      // Compiler error, immutable read only property.
    }
}

var c = new C( args ... );
MyCache.Add(c);            // This is ok.
c.c = ...;                 // Compiler error, immutable read only property.

Do not store references of partially constructed objects
This may sound silly, but is a long standing gotcha in Java and also in other languages, more close to metal. See the size of code for double-checked locking examples in Wikipedia!
The misconception is that:
singletons[i].reference = new Singleton();

means:

Allocate instance
Run constructor
Store object reference

But in multi-threaded C/C++/Java this also means:

Allocate instance
Store object reference
Run constructor

So an another thread can access singletons[i].reference and get a reference to a non constructed object. This invalidates all invariants!
For example, any non-null object property can now return null values. How bad is that?
Allow constructors to fail graciously
Running computations on a constructor means that these computations can fail. But if the only way to communicate these failures is to throw exceptions, the code may end up cluttered with exception handling code, even these failures are expected.
A gentle way is to allow constructors to fail more graciously
def constructor ( self? , args... )
{
    // Note the ? on self parameter.
    // This means that this constructor may not create an object.

    var memory = malloc( 1000 );
    if ( memory == null )
        return null;
}

And by so, code now can be more simple:
var fileinfo = new FileInfo(path) ?? FileInfo.Empty;

// or

var fileinfo = new FileInfo(path); // fileinfo is FileInfo?
if ( fileInfo ) { ... }            // fileinfo is FileInfo inside if
else { ... }                       // Failed to obtain file fin

Allow constructors to return errors
Same as above, buy we are interested on why FileInfo constructor failed.
def constructor ( args... ) self , error
{
    // Multiple returns on constructors are always disjoint unions
    // Alternative, explicit syntax:
    //
    //     def constructor () self |,| error

    if ( ... )
        return self;
    return (error) err;
}

var info, err = new FileInfo(path);

Console.Log( info.FileSize ); // Compiler error, disjoint type direct access.
throw err;                    // Compiler error, disjoint type direct access.

if ( info ) { ... } // Ok
else throw err;     // Ok
```
> **Comments:**
> **username_1** (2024-06-12): Swift and Objective-C have something similar to the last one; In Swift it's spelled `init?(...)`, and in Objective-C it's done by marking the return type as `instancetype _Nullable` or `instancetype __nullable` rather than just `instancetype`.  
> **username_8** (2024-06-14): "But in multi-threaded C/C++/Java this also means […]" – Even in Java? I find that incredibly surprising! My understanding of that statement would have been that the expression `new Singleton()` doesn't finish evaluating until the constructor has finished running, and the assignment doesn't occur until its right-hand side has finished evaluating. Which of these is not true for Java?  
---
### Answer by username_9 on 2024-06-12. Score: 5
The simplest method is to by default initialize to *something* as soon as the lifetime of an object begins.

In D this is the `Foo.init` value which every type has which gets memcopyd onto the variable when the lifetime begins. It can be customized in the definition of the struct. You can also just pick zero as initial value for everything.

Then you can opt-out by declaring the initial value of your variable as void: `Foo f = void;`

Straight forward compiler optimizations will let dead stores (where a field gets written twice without being read inbetween) get optimized out in the common case.

Using this scheme means that the programmer will need to opt-into uninitialized values.

Designing a constructor around making an object immutable after construction can be done by making the constructor the exception to not being able to write to the non-mutable fields (the others are still default init where it matters). Or by delaying actually writing the fields until a designated point in the constructor. Then you have a preamble that computes the actual fields after which the fields get written and the lifetime officially begins, and a epilogue where it might fit itself into a datastructure as it might need.
---
### Answer by username_10 on 2024-06-12. Score: 5
I'd like to add an interesting solution Bob Nystrom used in one of his languages, Magpie (explained in [this article](https://journal.stuffwithstuff.com/2010/12/14/the-trouble-with-constructors/)):

Basically, every class has a factory method, `new`, which by default simply creates an object using the given parameters and can be overridden to supply some more complex initialization logic. The trick is that within these methods, there's no way to actually access any instance variables of the new object, since it hasn't actually been created yet!

Instead, at the end of that method you call another, built-in method called `construct`, which takes in a record (basically a map) containing a key and value for every instance variable, and returns a new instance of the specific class initialized accordingly. That way, instance creation is *atomic*, meaning it is completely prohibited by design to perform operations on a *partially initialized* object.
> **Comments:**
> **username_4** (2024-06-12): Rust's approach, too, is to make the creation of structs an atomic operation. The rest of the details (that `construct` is a named function rather than using the name of the type, that `new` has a mandated name and a default implementation) are different, but the mechanism for preventing partial initialisation is the same.  
> **username_10** (2024-06-13): @kaya3 True, it is functionally pretty much equivalent. Though the semantics is different (as you're already "inside the object" instead of creating it "from the outside") and it's a nice workaround for a dynamically typed language.  
> **username_4** (2024-06-13): I'd argue that you aren't "inside" the object because there is no `this` to use; the object is created by `construct`, so there is no object for most of `new` despite its name.  
> **username_5** (2024-06-15): As mentioned by kaya3, you are not "inside" the object in Rust, because factory functions ("constructors") _do not take a `self`_ parameter. The only "constructors" baked into the language are `Type` (for types without fields), `Type(...)` (for types with unnamed fields), and `Type { ... }` (for types with named fields). And those are atomic.  
---
