Initializers

An initializer constructs a new instance of a struct. Kestrel synthesizes one for you by default, but you can write your own when the default isn't enough.

The default memberwise initializer

If you don't write an init, you get one for free that takes one labeled argument per field:

struct Point { let x: Int let y: Int } let p = Point(x: 0, y: 0);

The memberwise initializer always takes every field — there's no way to skip one. To make a parameter optional, write your own init and give the parameter a default value (defaulted parameters must come after the required ones):

struct Config { var port: Int let strict: Bool init(strict strict: Bool, port port: Int = 8080) { self.port = port; self.strict = strict; } } let c = Config(strict: true); // port defaults to 8080 let d = Config(strict: false, port: 9000); // override

Custom initializers

When the default doesn't fit — you want to compute a field, validate input, or pick from multiple inputs — write your own:

struct Span { let lower: Int let upper: Int init(from start: Int, length length: Int) { self.lower = start; self.upper = start + length; } } let r = Span(from: 10, length: 5);

Inside init, every let and var field must be assigned before the initializer returns. The compiler checks this — you can't accidentally leave a field uninitialized.

Writing any custom init — even one declared in an extend block — replaces the synthesized memberwise initializer. If you still want memberwise-style construction, declare it yourself alongside the custom one.

Multiple initializers

You can have several inits with different labels — same overloading rules as ordinary functions:

struct Vector { let x: Float64 let y: Float64 init(x x: Float64, y y: Float64) { self.x = x; self.y = y; } init(angle angle: Float64, length length: Float64) { self.x = length * angle.cos(); self.y = length * angle.sin(); } } let v = Vector(x: 3.0, y: 4.0); let u = Vector(angle: 1.57, length: 10.0);

Failable construction

If construction can fail, mark the initializer with ?. A failable init produces an Optional of the type; return null inside it signals failure:

struct User { let name: String init(parsing raw: String)? { if raw.isEmpty { return null } self.name = raw; } } let u = User(parsing: "ada"); // User? — .Some(User(...)) let bad = User(parsing: ""); // .None

This is the same pattern the standard library uses for Int(parsing:) and friends. The failure is visible in the result type, so the call site has to handle it. A static factory method returning Optional or Result works too, when you want a descriptive name or error details.