If I have a swift struct
struct Person {
let firstName: String
let lastName: String
let gender: Gender
let height: Float
let weight: Float
let age: Int
}
If have an object a
of type Person
, from time to time, only one of the field changed, like lastName
when the person gets married, age when the person had a birthday etc. but I don't want to create a mutating func
for each of the fields, nor do I want to write a lot of boilerplate to construct a new struct passing in all those fields again. Is there a better to to achieve something like
let newPerson = oldPerson.copy(lastName = "Married")
I know this is a really neat feature in scala with case classes, but I have yet to see this feature used in swift. May I know how can I achieve this? Do you guys think it should be a baked in feature in swift? Maybe we can make a swift evolution proposal?
I don't know if there is an elegant solution. One option would be to define a copy method which takes optional parameters for each property, which default to the current property values:
struct Person {
let firstName: String
let lastName: String
let age: Int
func copy(firstName: String? = nil, lastName: String? = nil, age: Int? = nil) -> Person {
return Person(firstName: firstName ?? self.firstName,
lastName: lastName ?? self.lastName,
age: age ?? self.age)
}
}
let john = Person(firstName: "John", lastName: "Doe", age: 29)
let jane = john.copy(firstName: "Jane")
print(jane) // Person(firstName: "Jane", lastName: "Doe", age: 29)
A copy
function of Person
can naturally have default value for its arguments, which would allow calling it with only a subset of the properties of person. The problem is that we may not use self
to refer to what values we'd like to use as default. We can, however, refer to static members of a class (fir convenience, say static members of Person
), which would allow us to create something along the lines
struct Person {
static var currentObjFirstName: String = ""
static var currentObjLastName: String? = ""
// ...
static var currentObjAge: Int = 0
let firstName: String
let lastName: String?
// ...
let age: Int
init(firstName: String, lastName: String?, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
func prepareCopy() -> Person {
Person.currentObjFirstName = self.firstName
Person.currentObjLastName = self.lastName
Person.currentObjAge = self.age
return self
}
func copy(firstName firstName: String = Person.currentObjFirstName,
lastName: String? = Person.currentObjLastName,
age: Int = Person.currentObjAge) -> Person {
return Person(firstName: firstName, lastName: lastName, age: age)
}
}
Example usage:
let foo = Person(firstName: "foo", lastName: "bar", age: 42)
let bar = foo.prepareCopy().copy(age: 21)
print(bar) // Person(firstName: "foo", lastName: Optional("bar"), age: 21)
let baz = foo.prepareCopy().copy(lastName: nil)
print(baz) // Person(firstName: "foo", lastName: nil, age: 42)
Possibly this solution goes under your description as "... boilerplate to construct a new struct passing in all those fields again" , though, and apart from that, prepareCopy()
above isn't very pretty...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.