generic์„ ๋ณด๋ฉด์„œ ๋ช‡๋ช‡ ๊ณณ์—์„œ where ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ณด์•˜๋‹ค. ํ•œ๋ฒˆ ์ •๋ฆฌํ•ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์•„ ๊ณต์‹ ๋ฌธ์„œ์„ ์ฝ์–ด๋ณธ๋‹ค.

Generic Where Clauses

generic์—๋„ where ์กฐํ•ญ์„ ๋„ฃ์–ด ๋ฐ›๋Š” ํƒ€์ž…์— ์„ธ๋ถ€ ์ œ์•ฝ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

func allItemsMatch<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.Item == C2.Item, C1.Item: Equatable {
 
        // Check that both containers contain the same number of items.
        if someContainer.count != anotherContainer.count {
            return false
        }
 
        // Check each pair of items to see if they're equivalent.
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
 
        // All items match, so return true.
        return true
}
 
extension Array: Container {} // ์ด๋ฏธ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ์ค€์ˆ˜ํ•˜๊ณ  ์žˆ์Œ
 
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
 
var arrayOfStrings = ["uno", "dos", "tres"]
 
if allItemsMatch(stackOfStrings, arrayOfStrings) {
    print("All items match.")
} else {
    print("Not all items match.")
}
// Prints "All items match."

์ผ๋ฐ˜ where ์ ˆ์€ where ํ‚ค์›Œ๋“œ๋กœ ์‹œ์ž‘ํ•œ ๋‹ค์Œ, associated type์— ๋Œ€ํ•œ ์ œ์•ฝ ์กฐ๊ฑด ๋˜๋Š” associated type ๊ฐ„์˜ ๋™๋“ฑ ๊ด€๊ณ„ ๋“ฑ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. allItemsMatch ํ•จ์ˆ˜๋Š” ๋‘๊ฐœ์˜ Container ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜ํ•˜๋Š” ๊ตฌํ˜„์ฒด๋ฅผ ๋ฐ›์•„ ๋‚ด์šฉ๋ฌผ์ด ๋ชจ๋‘ ๊ฐ™์€์ง€ ๋น„๊ตํ•œ๋‹ค. ์ด ๋•Œ, ๋‘๊ฐœ์˜ Container์˜ associted type์ธ Item ํƒ€์ž…์ด ์„œ๋กœ ๊ฐ™์€์ง€, ๊ทธ๋ฆฌ๊ณ  Eqatable ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์ œ์•ฝ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ์ด๋Ÿฐ ๊ธฐ๋Šฅ์€ compile time์— ํƒ€์ž… ์ฒดํฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— type safeํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

Extensions with a Generic Where Clause

where์ ˆ์€ extension์—๋„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

struct Stack<Element>: Container {
    // original Stack<Element> implementation
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
    // conformance to the Container protocol
    mutating func append(_ item: Element) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Element {
        return items[i]
    }
}
 
extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        guard let topItem = items.last else {
            return false
        }
        return topItem == item
    }
}

์œ„์˜ ๋‹จ์ˆœํ•œ stack์˜ ๊ฒฝ์šฐ์—๋Š” element๊ฐ„์˜ ๋™๋“ฑ ์—ฌ๋ถ€("==")๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์—†์—ˆ๋‹ค. ์ด๋Š” Item์ด Equable protocol์„ ์ค€์ˆ˜ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์‹คํ–‰ํ•˜๋ฉด compile error๊ฐ€ ๋œจ๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ, ๋ถ€๋ถ„์ ์œผ๋กœ extension์— Item type์— where๋ฅผ ํ†ตํ•œ ์ œ์•ฝ์„ ๊ฑธ์–ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

if stackOfStrings.isTop("tres") {
    print("Top element is tres.")
} else {
    print("Top element is something else.")
}
// Prints "Top element is tres."

๋งŒ์•ฝ equtable์„ ์ค€์ˆ˜ํ•˜๊ณ  ์žˆ์ง€ ์•Š์€ ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค๋ฉด ์–ด๋–จ๊นŒ?

struct NotEquatable { }
var notEquatableStack = Stack<NotEquatable>()
let notEquatableValue = NotEquatable()
notEquatableStack.push(notEquatableValue)
notEquatableStack.isTop(notEquatableValue)  // Error

๋‹น์—ฐํ•˜๊ฒŒ๋„ compile error๊ฐ€ ๋œฌ๋‹ค.

Extension with a Protocol where clause

์ด๋ ‡๊ฒŒ extension์— where์„ ์‚ฌ์šฉํ•ด์„œ ์ถ”๊ฐ€์ ์ธ ์ œ์•ฝ์„ ๊ฑฐ๋Š” ๊ฒƒ์€ protocol์—์„œ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

extension Container where Item: Equatable {
    func startsWith(_ item: Item) -> Bool {
        return count >= 1 && self[0] == item
    }
}
 
extension Container where Item == Double {
    func average() -> Double {
        var sum = 0.0
        for index in 0..<count {
            sum += self[index]
        }
        return sum / Double(count)
    }
}
 
if [9, 9, 9].startsWith(42) {
    print("Starts with 42.")
} else {
    print("Starts with something else.")
}
// Prints "Starts with something else."
 
print([1260.0, 1200.0, 98.6, 37.0].average())
// Prints "648.9"
 

protocol์€ extension์— ๊ธฐ๋ณธ ๋™์ž‘์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๊ฒฝ์šฐ where์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ์ถ”๊ฐ€์ ์ธ ์ œ์•ฝ์„ ๊ฑธ๋ฉด์„œ ๊ธฐ๋ณธ๋™์ž‘๊นŒ์ง€ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ([9, 9, 9] ๊ฐ€ ์ฒ˜๋ฆฌ๊ฐ€๋Šฅ ํ•œ ๊ฒƒ์€ ์œ„์—์„œ extension Array: Container {}๋ฅผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.)

Reference