Initialization

Each variable or struct/class field must be initialized, either explicitly or implicitly. Absence of initialization isn’t allowed in safe code, the compiler ensures that all is initialized.

There are several initializer kinds, that are applicable dependent on the type of initialized value.

Expression initialization

This is the simplest initialization kind. An expression is defined after = for a variable.

var i32 x= 0;
var f32 y= 1.0f - 72.5f / 2.0f;

In combined initializers there is no =, expression initialization happens if only expression itself is specified.

Sequence initialization

It is used for initialization of arrays and tuples. It consists of sequence of initializers separated by comma inside []. The number of elements in the initializer should be equal to the number of elements in an array/in a tuple.

var [ i32, 2 ] arr0[ 0, 1, 100 ]; // Initialize an array
var [ f32, 3 ] arr1[ 0.0f, -5.0f, 44.4f ]; // Initialize an array
var tup[ i32, bool ] tup0[ 5, false ]; // Initialize a tuple
var [ [ i32, 2 ], 3 ] arr2[ [ 5, 7 ], [ 50, 70 ], [ 500, 700 ] ]; // Initialize a two-dimensional array
var tup[ f32, [ i32, 3 ] ] tup1[ 0.0f, [ 0, 0, 0 ] ]; //Initialize a tuple with an array inside

constructor initialization

It’s used for structs/classes initialization when they have constructor(s). It consists of () and comma-separated arguments list inside.

struct S
{
    fn constructor(){}
    fn constructor(i32 in_x, i32 in_y) (x= in_x, y= in_y) {}
    i32 x= 0;
    i32 y= 0;
}

fn Foo()
{
    var S s0(); // Call the constructor with zero parameters
    var S s1( 5, 7 ); // Call the constructor with two parameters
    var [ S, 2 ] arr0[ (), ( 99, 55 ) ]; // Call different constructors for different array elements
}

For fundamental types it’s possible to perform type conversion via constructor initialization or just make a copy.

var i32 x= 0, x_copy(x);
var f32 y(x); // conversion i32 -> f32
var f64 z(y), w(x_copy); // conversions f32 -> f64 and i32 -> f64
var u16 cc("B"c8); // conversion char8 -> u16
var char32 cccc(66); // conversion i32 -> char32
var u16 short_int(574);
var i64 long_signed_int(short_int); // conversion u16 -> i64

Constructor initialization allows also to make copies for arrays and tuples.

var [i32, 2] i0 = zero_init, i1(i0);
var tup[bool, f32] t0[true, 14.5f], t1(t0);

Member-by-member initializer

It’s used for structs initialization. It consists of {} and comma-separated list of initializers for fields. Each field is specified starting with ..

struct S
{
    i32 x;
    i32 y;
    i32 z= 0;
}

fn Foo()
{
    var S s0{ .x= 0, .y= 0, .z= 0 }; // Initialize all fields in their definition order
    var S s1{ .y= 1, .x= 0 }; // Order may be different. For fields with default initializer an explicit initializer may not be specified.
    var tup[ S ] t[ { .z= 0, .x= 2, .y= 2 } ];
}

It’s possible to use this initializer kind in expression context in order to construct temporary values of struct types.

struct S{ i32 x; i32 y; }
fn Bar(S s);
fn Foo()
{
    Bar( S{ .x= 42, .y= 24 } ); // Create temporary value of "S" type by initializing its fields, than pass it into a function.
}

Empty initialization

It’s allowed to specify no initializer, if there is a default-initialization for a given type.

struct S
{
    fn constructor() (x= 0, y= 0) {} // Default-constructor

    i32 x;
    i32 y;
}

fn Foo()
{
    var S s; // Default constructor will be called
    var [ S, 8 ] arr; // Default constructor will be called for each array element
}

Zero initialization

It’s used for zero-initialization for numbers, false initialization for bool values, initialization with first element for enums, \0 for char types, null for pointer types. This initialization is not allowed for classes. It is allowed for structs which contain no reference fields and explicit constructors except copy constructor. Zero initialization is specified with usage of zero_init keyword.

struct S
{
    i32 x;
    i32 y;
}

enum E{ A, B, C, }

fn Foo()
{
    var i32 x= zero_init;
    var S s0= zero_init; // Zero whole struct
    var S s1{ .x= 4, .y= zero_init }; // Zero one of the struct fields
    var [ f32, 128 ] arr0= zero_init; // Zero whole number array
    var [ S, 3 ] arr1= zero_init; // Zero whole struct array
    var [ S, 2 ] arr2[ { .x= 1, .y= 1 }, zero_init ]; // Zero only one struct in an array
    var tup[ E, bool, i32, i64, f64 ] t= zero_init; // Zero whole tuple
}

Uninitialized initialization

It allows to skip initialization. But this is allowed only in unsafe blocks. it’s recommended to use it only if it’s absolutely necessary for performance reasons and only if values will be initialized later.

fn Foo()
{
    unsafe
    {
        var i32 x= uninitialized;
    }
}

Constructor initialization list

Constructors of structs and classes may have fields initialization list. In this list initializers for fields are specified. Inside initializers of one fields other fields (that was already initialized) may be used. Fields with no initializer specified will be default-initialized before explicitly-initialized fields. The fields order is irrelevant, the only constraint is that fields should not be accessed before they will be initialized.

struct S
{
    fn constructor()
    ( y= z + 1, x= y / 2 ) // "z" is initialized at the beginning explicitly, that "y" is initialized (with usage of "z"), that "x" is initialized with usage of previously initialized "x" value
    {}

    i32 x;
    i32 y;
    i32 z= 0;
}

Fields own initializers

Struct and class fields may have own initializers, that are defined together with field definition. A field will be initialized with it if no other initializer is specified.

struct A
{
    // A default-constructor will be generated for this struct, because all fields have initializers
    i32 x= 100;
}

struct Vec
{
   fn constructor() () {} // All fields are initialized with their own initializers
   fn constructor(i32 in_x, i32 in_y) (x= in_x, y= in_y) {} // Fields are intialized with explicit initializers, fields own initializers are not used
   i32 x= 0;
   i32 y= 0;
}

struct SimpleVec
{
    A a; // Field has default initializer since "A" has default-constructor
    i32 x= 0;
    i32 y= 0;
}

fn Foo()
{
    var A a; // Generated default-constructor will be called. It was generated since all fields have initializers.
    var Vec v0(), v1, v2( 10, -5 ); // In first two cases own field initializers will be used, in the third - explicitly-specified initializer values
    var SimpleVec v3{}; // In the struct initializer fields initializers are not specified thus own field initializers will be used
}