1

I'm trying to write a custom assignment operator in Swift 3 that will allow me to set a variable and update its properties all at once. Like so,

protocol BatchSettable { }

infix operator =<

public func =< <T: BatchSettable>(lhs: inout T?, rhs: (inout T) -> Void) {
    if var copy = lhs {
        rhs(&copy)
        lhs = copy
    } else {
        lhs = nil
    }
}

This would, in theory, allow me to write

struct MyStruct: BatchSettable {
    var someBoolProperty: Bool?
    var someStringProperty: String?
}

self.myStruct =< {
    $0.someBoolProperty = false
    $0.someStringProperty = "someString"
}

instead of

var myVar = self.myStruct
myVar.someBoolProperty = false
myVar.someStringProperty = "someString"
self.myStruct = myVar

My intention here is to be able to update multiple fields on a value-type property (struct) without triggering didSet multiple times.

The operator itself compiles with no errors, but when I try to use it I get

Cannot assign to property: '$0' is immutable

Any ideas?

edc1591
  • 10,146
  • 6
  • 40
  • 63
  • 2
    This is a bug – see [SR-1976](https://bugs.swift.org/browse/SR-1976) & [SR-1811](https://bugs.swift.org/browse/SR-1811). The simple fix is to add an explicit parameter list to the closure (e.g `myStruct =< {(myStruct: inout MyStruct) in ... }`). – Hamish Nov 21 '16 at 17:07
  • @Hamish How do you _know_ this stuff? Do you spend your free time reading the bugbase?? :)))) – matt Nov 21 '16 at 17:08
  • 3
    @matt Now I'm not *that* sad ;) It's because [it's come up before](http://stackoverflow.com/questions/40201844/global-function-sequencestatenext-and-type-inference) if you recall :) – Hamish Nov 21 '16 at 17:09
  • @Hamish Oh, okay, so the secret is that you just remember absolutely everything! :)))) – matt Nov 21 '16 at 17:11
  • @Hamish since your answer in the linked thread (somewhat) answers this question as well, should we dupe mark it? – dfrib Nov 21 '16 at 17:19
  • @dfri I was just wondering the same thing – I guess so? Was hoping that OP would show up to confirm that he's happy with it. – Hamish Nov 21 '16 at 17:24
  • 2
    @Hamish Since your first comment fully circumvents the bug (and fixes OPs issue), I believe we may presume that he will be, and proceed with setting up the dupe link :) – dfrib Nov 21 '16 at 17:25
  • 1
    @matt shoutout for possible use of mighty dupe hammer. – dfrib Nov 21 '16 at 17:26
  • Assuming this is the only solution, I'd say mark it as a dupe. Although, I wish there was a way where I didn't have to explicitly define the closure parameters. I guess we just wait for the bug fix... – edc1591 Nov 21 '16 at 17:27
  • Confused, (lhs: inout T?, rhs: (inout T) -> Void) should it be (lhs: inout T?, rhs: (inout T)) -> Void – antonio081014 Nov 21 '16 at 17:29
  • @antonio081014 Nope, `(inout T) -> Void` is the closure passed in as the right side of the operation. The function itself does not explicitly declare a return type since it's an assignment operator. – edc1591 Nov 21 '16 at 17:30
  • 1
    @edc1591 It's the only solution I'm aware of, and SR-1976 is marked as "In progress", so hopefully it'll be something that's fixed soon :) – Hamish Nov 21 '16 at 17:31
  • 2
    @edc1591 Okay, I'll mark it as a dupe (because we need the clear pointer to the other answer) and if you decide I'm wrong, just let me know and I'll undupe it. – matt Nov 21 '16 at 17:35

0 Answers0