I have a swift struct somewhat like this:
struct LogicalState {
let a: String?
let b: Bool
let c: Int
}
and a mutable instance of this state. Note the properties within the state are all let
, so the struct itself is immutable.
var _state: LogicalState
What I'd like to do is enforce a pattern where updating the state is allowed, but all updates must be atomic - I do NOT want to simply make a, b, and c mutable, as that would allow a and b to change independently. I need to control the updates and apply validation (for example, to enforce that if you change a
, you also must change b
at the same time)
I can do this by simply overwriting the whole struct
_state = LogicalState(a: "newA", b: false, c: _state.c)
However, as you can see, having to explicitly reference the old state for the properties that don't change (_state.c
) is annoying and problematic, especially when you have more properties. My real-world example has something like 10.
In kotlin, they have "data classes" which expose a "copy" method, which lets you change only the parameters you want. If swift supported such a thing, the syntax would look like this
func copy(a: String? = self.a, b:Bool = self.b, c:Int = self.c) ...
The problem is, the = self.a
syntax doesn't exist in swift, and I'm not sure of what other options I have?
Any solution on how to solve this would be much appreciated
Yep, you're right, structs are not immutable. The thing about structs is that they are values. That means every variable is considered a copy, and its members are isolated from changes made to other variables. Structs are not copied on mutation.
Copy-on-Write is a computing technique available in the Swift standard library for value types such as collections and structs . Essentially, it defers the copying of collections and structures until when it's actually needed.
Unlike other programming languages, Swift doesn't require you to create separate interface and implementation files for custom structures and classes. In Swift, you define a structure or class in a single file, and the external interface to that class or structure is automatically made available for other code to use.
In Swift, structs are value types whereas classes are reference types. When you copy a struct, you end up with two unique copies of the data. When you copy a class, you end up with two references to one instance of the data. It's a crucial difference, and it affects your choice between classes or structs.
Think, you can extend the struct with a copy(...) method taking nil values as default and replacing them with instance ones while using non-nil otherwise. E.g. something like this:
extension LogicalState {
func copy(a: String? = nil, b: Bool? = nil, c: Int? = nil) -> LogicalState {
return LogicalState(a: a ?? self.a, b: b ?? self.b, c: c ?? self.c)
}
}
So you can use it to copy instance while varying the needed params:
let state = LogicalState(a: "A", b: false, c: 10)
let stateCopy1 = state.copy(c: 30)
let stateCopy2 = state.copy(a: "copy 2")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With