- Proposal: SE-0242
- Author: Alejandro Alonso
- Review Manager: Ted Kremenek
- Status: Implemented (Swift 5.1)
- Decision Notes: Rationale
- Implementation: apple/swift#19743
This proposal aims to solve a simple outstanding problem with the way the Swift compiler currently synthesizes the memberwise initializer for structures by synthesizing default values for properties with default initializers.
This is mentioned in the "State of the Memberwise Initializer" forum post: here
Currently the Swift compiler is able to synthesize a fairly basic memberwise initializer for structures.
struct Dog {
var age: Int
var name: String
}
The compiler is able to synthesize a memberwise iniailizer for this structure which simply looks like this:
init(age: Int, name: String)
But, lets say we want all dogs to have a default value of 0
for the age:
struct Dog {
var age: Int = 0
var name: String
}
A user might naively try using this default value when constructing their Dog
instance:
// I just want to set the name of Dog, sparky is a newborn
let sparky = Dog(name: "Sparky")
To their surprise, they can't. missing argument for parameter 'age' in call
. Using the compiler synthesized memberwise initializer has turned to become a nuisance rather than a nice removal of boilerplate. In many cases the user may optionally just define their own initializer with a default value for the age parameter.
struct Dog {
var age: Int = 0
var name: String
// This is defined because the Swift compiler can't generate default values for properties with an initial value
init(age: Int = 0, name: String) {
self.age = age
self.name = name
}
}
I propose simply doing the obvious and synthesizing default values for properties with default initializers in the memberwise initializer. Simple code like the following will simply work:
struct Dog {
var age: Int = 0
var name: String
// The generated memberwise init:
init(age: Int = 0, name: String)
}
// This now works
let sparky = Dog(name: "Sparky") // Dog(age: 0, name: "Sparky")
The following example displays the memberwise initializer being produced by the compiler with a combination of variables with default values.
struct Alphabet {
var a: Int = 97
let b: String
var c: String = "c"
let d: Bool = true
var e: Double = Double.random(in: 0 ... .pi)
// The generated memberwise init:
init(
a: Int = 97,
b: String,
c: String = "c",
e: Double = Double.random(in: 0 ... .pi)
)
}
Notice the d
variable does not get an entry in the memberwise initializer because it is a constant whose value is already assigned. This behavior already exists with the current initializer.
In the case where multiple variables are being initialized together, we cannot generate a default value for them in the memberwise initializer. For example:
struct Person {
var (firstName, lastName) = ("First", "Last")
// The generated memberwise init:
init(firstName: String, lastName: String)
}
This change does not alter the requirements needed to synthesize the memberwise initializer, but rather if we can synthesize the memberwise initializer, also synthesize default values for properties with default initializers. Note that we can only synthesize values for variables that have declared default initializers and not constants.
This is a purely additive feature, thus source compatibility is not affected.
This feature does not alter ABI, thus ABI stability is not affected.
As the memberwise initializer is only synthesized as an internal initializer, this feature does not affect API resilience.
We could simply not do this and save this proposal for a solution much larger in regards to fixing more problems the memberwise initializer has. The downside is that we put off obvious changes like this for much longer because of wanting to solve a bigger problem. I agree we should solve the bigger problems, but by solving problems like this it aids in the solution of the larger problem.