Class Member Initialisation

C++ does not require that you initialise all variables in every constructor.

  • A member that is a C++ class with its own default constructor doesn’t need to be initialised
  • A member that is a C++ class without a default constructor must be explicitly initialised.
  • A member that is a reference must be explicitly initialised
  • Primitive types, including pointers do not have to be initialised although the compiler may warn if they are not
  • Members do not have to be initialised in the order they are declared

Some compilers may issue warnings if you forget to initialise members or their ordering, but they will still compile
the code.

C++11 allows classes to have default member initializers which are used in the absence of a constructor setting the
value to something else:

  1. class Coords {
  2. public:
  3. double x = 0.0;
  4. double y = 0.0;
  5. double z = 0.0;
  6. // 2D initializer, x and y are set with the inputs, z is set to 0
  7. Coords(double x, double y) : x(x), y(y) {}
  8. };

This is obviously a lot easier to read and ensures that if we have multiple constructors that we don’t have to initialize
members if the default value will do.

How Rust helps

You MUST initialise all members of a struct. If your code does not initialise a struct you will get a compiler error.

This will not compile:

  1. struct Alphabet {
  2. a: i32,
  3. b: u32,
  4. c: bool,
  5. }
  6. let a = Alphabet { a: -10, c: true };

If you try you will get an error like this:

  1. rustc 1.13.0-beta.1 (cbbeba430 2016-09-28)
  2. error[E0063]: missing field `b` in initializer of `main::Alphabet`
  3. |
  4. 9 | let a = Alphabet { a: -10, c: true };
  5. | ^^^^^^^^ missing `b`

Forcing you to initialise the members of the struct ensures the struct is always in a consistent predictable state.

Ordering of initialisation does not matter providing all of the fields are set.

Structs often implement a new() function which encapsulates this initialisation and acts like a constructor in C++, e.g.

  1. struct Coord {
  2. pub x: f64,
  3. pub y: f64,
  4. pub z: f64,
  5. }
  6. impl Coord {
  7. pub fn new(x: f64, y:f64) {
  8. Coord { x: x, y: y, z: 0f64 }
  9. }
  10. }
  11. ///...
  12. let coord1 = Coord::new(100f64, 200f64);

Alternatively the struct might implement one or more From<> traits:

  1. impl From<(f64, f64)> for Coord {
  2. fn from(value: (f64, f64)) -> Coord {
  3. Coord { x: value.0, y: value.1, z: 0.0 }
  4. }
  5. }
  6. impl From<(f64, f64, f64)> for Coord {
  7. fn from(value: (f64, f64, f64)) -> Coord {
  8. Coord { x: value.0, y: value.1, z: value.2 }
  9. }
  10. }
  11. //...
  12. let coord = Coord::from((10.0, 20.0));
  13. let coord = Coord::from((10.0, 20.0, 30.0));

There can be multiple From trait implementations so we can implement a form of polymorphism.