【Swift】 クロージャーで呼び出し元 struct のプロパティを更新する

# 【Swift】 クロージャーで呼び出し元 struct のプロパティを更新する

## カスタム Struct の例

自身または更新させたいプロパティを参照で渡す関数を定義する。

```swift
protocol Editable {}
extension Editable {
    mutating func edit( _ editor: ( inout Self ) -> Void  ) {
        editor(&self)
    }
}
struct SomeParent {
    var child: SomeChild = SomeChild(
        val: 1
    )
}
struct SomeChild: Editable {
    var val: Int
}

var parent = SomeParent()
parent.child.edit {
    $0.val += 1
}
print(parent.child.val) // 2 を出力
```

## Array の例

要素を参照で渡す関数を定義する。

```swift
extension Array {
    mutating func updateEach( _ updater: ( inout Element ) -> Void ) {
        for i in 0..<self.count {
            updater(&self[i])
        }
    }
}

var array =  [1, 2, 3, 4, 5]
array.updateEach {
    $0 += 1
}
print(array) // [2, 3, 4, 5, 6]
```

### 部分的な更新

fatalError の代わりに throw する例外を用意しておく

```swift
extension Array {
    enum ArrayError: Error, LocalizedError {
        case indexOutOfBounds( index: Int )
        case invalidRange( range: Range<Index>)
        case invalidClosedRange( range: ClosedRange<Index>)
        var errorDescription: String? {
            switch self {
                case .indexOutOfBounds( let index ):
                    return "Index out of bounds: \(index)"
                case .invalidRange( let range ):
                    return "Invalid range: \(range)"
                case .invalidClosedRange( let range ):
                    return "Invalid range: \(range)"
            }
        }
    }
}
```


##### Index 指定

```swift
extension Array {
    mutating func update( index: Int, _ updater: ( inout Element ) -> Void ) throws {
        guard self.indices.contains(index) else {
            throw Array.ArrayError.indexOutOfBounds(index: index)
        }
        updater(&self[index])
    }
}
var array =  [1, 2, 3, 4, 5]
try array.update( index: 2 ) {
    $0 += 10
}
print(array) // [1, 2, 13, 4, 5] 
```

##### Range ( n..<m ) 指定

```swift
extension Array {
    mutating func update( range: Range<Index>, _ updater: ( inout Element ) -> Void ) throws {
        guard self.indices.contains(range) else {
            throw Array.ArrayError.invalidRange(range: range)
        }
        for i in range {
            updater(&self[i])
        }
    }
}
try array.update(range: 1..<3) {
    $0 += 20
}
print(array) // [1, 22, 23, 4, 5]
```

##### ClosedRange ( n...m ) 指定

```swift
extension Array {
    mutating func update( range: ClosedRange<Index>, _ updater: ( inout Element ) -> Void ) throws {
        guard self.indices.contains(range) else {
            throw Array.ArrayError.invalidClosedRange(range: range)
        }
        for i in range {
            updater(&self[i])
        }
    }
}
try array.update(range: 1...3) {
    $0 += 30
}
print(array) // [1, 32, 33, 34, 5]
```

## Dictionaryの例

Arrayと同様、Valueを参照で渡す関数を定義する。

```swift
extension Dictionary {
    mutating func updateEach( _ updater: ( inout Value ) -> Void ) {
        for key in self.keys {
            updater(&self[key]!)
        }
    }
}
var dic = [
    "1" : 1,
    "2" : 2,
]
dic.updateEach {
    $0 += 1
}
print(dic) // ["2": 3, "1": 2]
```

##### Key 指定

```swift
extension Dictionary {
    mutating func update( key: Key, _ updater: ( inout Value ) -> Void ) {
        guard keys.contains(key) else {
            return
        }
        updater(&self[key]!)
    }
}
var dic = [
    "1" : 1,
    "2" : 2,
]
dic.update(key: "1") {
    $0 += 100
}
print(dic) // ["1": 101, "2": 2]
```