Swift에서 'Hashable' 프로토콜을 준수하지 않는 문제 해결하기!

작성일 :

Swift에서 'Hashable' 프로토콜을 준수하지 않는 문제 해결하기

Swift에서 객체를 컬렉션의 키로 사용하거나 집합(Set)의 요소로 사용하려면 해당 객체가 Hashable 프로토콜을 준수해야 합니다. 그러나 때로는 "Swift does not conform to protocol 'Hashable'" 오류 메시지를 볼 수 있습니다. 이 글에서는 Hashable 프로토콜의 개념과 해당 프로토콜을 준수하지 않는 문제를 해결하는 방법에 대해 자세히 설명하겠습니다.

Hashable 프로토콜이란?

Hashable 프로토콜은 객체를 해시 가능한 상태로 만들기 위한 프로토콜입니다. Hashable을 준수하는 객체는 해시 값을 제공할 수 있으며, 이는 해당 객체를 빠르게 비교하거나 검색하는 데 사용됩니다. Swift 표준 라이브러리의 많은 컬렉션 타입(예: DictionarySet)은 내부적으로 해시 값을 사용합니다.

Hashable 프로토콜의 요구 사항

Hashable 프로토콜을 준수하려면 객체는 hash(into:) 메서드를 구현해야 하며, 이는 객체의 속성 값을 해시 함수에 전달합니다. 또한, Equatable 프로토콜을 준수해야 하며, 이는 두 객체가 동일한지 비교할 수 있는 메서드를 제공합니다.

swift
protocol Hashable: Equatable {
    func hash(into hasher: inout Hasher)
}

'Hashable' 프로토콜 준수 문제 해결하기

기본 사용 예제

Hashable 프로토콜을 준수하는 객체를 정의하기 위해, 다음과 같이 hash(into:) 메서드를 구현할 수 있습니다.

swift
struct Person: Hashable {
    var name: String
    var age: Int

    func hash(into hasher: inout Hasher) {
        hasher.combine(name)
        hasher.combine(age)
    }
}

let person1 = Person(name: "Alice", age: 30)
let person2 = Person(name: "Bob", age: 25)

var peopleSet: Set<Person> = [person1, person2]
print(peopleSet)

이 예제에서는 Person 구조체가 Hashable 프로토콜을 준수하며, hash(into:) 메서드를 통해 nameage 속성을 해시 함수에 결합합니다.

자동 Hashable 구현

Swift 4.1 이후로, 컴파일러는 구조체가 모든 저장 속성이 Hashable을 준수하는 경우 자동으로 Hashable을 구현할 수 있습니다.

swift
struct Person: Hashable {
    var name: String
    var age: Int
}

let person1 = Person(name: "Alice", age: 30)
let person2 = Person(name: "Bob", age: 25)

var peopleSet: Set<Person> = [person1, person2]
print(peopleSet)

위 예제에서는 Person 구조체가 자동으로 Hashable을 준수합니다.

커스텀 타입에서 Hashable 구현

특정 클래스나 구조체가 Hashable 프로토콜을 준수하지 않는 경우, 이를 수동으로 구현해야 할 수 있습니다.

swift
class Car: Hashable {
    var make: String
    var model: String
    var year: Int

    init(make: String, model: String, year: Int) {
        self.make = make
        self.model = model
        self.year = year
    }

    static func == (lhs: Car, rhs: Car) -> Bool {
        return lhs.make == rhs.make && lhs.model == rhs.model && lhs.year == rhs.year
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(make)
        hasher.combine(model)
        hasher.combine(year)
    }
}

let car1 = Car(make: "Toyota", model: "Corolla", year: 2018)
let car2 = Car(make: "Honda", model: "Civic", year: 2020)

var carSet: Set<Car> = [car1, car2]
print(carSet)

이 예제에서 Car 클래스는 Hashable 프로토콜을 준수하도록 == 연산자와 hash(into:) 메서드를 구현합니다.

'Hashable' 프로토콜 준수 문제 발생 이유와 해결 방법

문제 원인

  1. 저장 속성의 타입이 Hashable을 준수하지 않음: Hashable을 준수하려면 모든 저장 속성이 Hashable을 준수해야 합니다.
  2. 클래스 상속: 클래스가 상속 구조를 가지면, 자동으로 Hashable을 준수할 수 없습니다. 이 경우 수동으로 구현해야 합니다.
  3. 커스텀 타입의 사용: 커스텀 타입이 Hashable을 준수하지 않으면 이를 수동으로 구현해야 합니다.

해결 방법

  1. 저장 속성 타입 확인: 모든 저장 속성이 Hashable을 준수하는지 확인하고, 필요시 이를 준수하도록 구현합니다.
  2. 수동 구현: 자동으로 Hashable을 준수하지 않는 경우, EquatableHashable 프로토콜을 수동으로 구현합니다.
  3. 상속 구조에서 Hashable 구현: 클래스 상속 구조에서는 EquatableHashable을 적절히 구현하여 프로토콜을 준수합니다.

결론

Swift에서 Hashable 프로토콜을 준수하지 않는 문제는 대부분 저장 속성의 타입이나 상속 구조와 관련이 있습니다. 이를 해결하려면, 모든 저장 속성이 Hashable을 준수하도록 하고, 필요시 EquatableHashable을 수동으로 구현해야 합니다. 이를 통해 Swift 컬렉션에서 객체를 효과적으로 사용할 수 있습니다.

더 많은 정보는 🔗 Apple Developer Documentation에서 확인할 수 있습니다.