[Swift] - Hashable 관련 이슈


protocol can only be used as a generic constraint becaues it has Self or associated type requirements


Hashable을 상속한 프로토콜 안의 멤버 변수의 타입을 또 hashable을 상속한 프로토콜로 설정했더니 아래와 같은 에러가 떴다.

Protocol '' can only be used as a generic constraint because it has Self or associated type requirements

그니까 프로토콜이 Self / associated type 요구사항을 가지고 있기 때문에 generic constraint로만 사용될 수 있다는 것이다.

예시 코드를 작성해봤다.

protocol Animal {
    var name: String { get }
    var age: Int { get }
    var owner: Owner { get }
}

final class AnimalClass: Animal {
    let name: String
    let age: Int
    let owner: Owner
    
    init(name: String, age: Int, owner: Owner) {
        self.name = name
        self.age = age
        self.owner = owner
    }
}


protocol Owner {
    var name: String { get }
    var address: String { get }
}

final class OwnerClass: Owner {
    let name: String
    let address: String
    
    init(name: String, address: String) {
        self.name = name
        self.address = address
    }
    
}

요 상황에서, Animal이 hashable을 상속하게 해보았다. Hashable 프로토콜은 Equatable 프로토콜을 상속하고 있기 때문에, Hashable 프로토콜을 상속하면 Equatable 프로토콜의 요구사항도 만족해야 한다.

image

이러면 아직 Owner 프로토콜은 아직 Hashable을 상속받고 있지 않기 때문에 에러가 또 발생하는 것을 볼 수 있다. 이를 해결하기 위해 Owner 프로토콜이 Hashable을 따르도록 수정해보았다.

이러면 이제 Animal 프로토콜과 클래스 쪽에서 이런 에러를 볼 수 있다.

image

관련해서 StackOverflow 을 참고해서 뭐가 문제인지 찾아봤다.

설명에 의하면 Hashable 프로토콜은 Equatable 프로토콜을 따르고 있고, Equatable 프로토콜은 아래의 요구사항을 가지고 있다.

func ==(lhs: Self, rhs: Self) -> Bool

그리고 이런 Self를 자기 자신 안에서 가진 프로토콜은 type constraint를 제외하고 사용할 수 없기 때문에 이런 에러가 발생한는 것이다. 그렇다면 왜 Self나 associated type을 가진 프로토콜은 type constraint에서만 사용이 가능한 것일까?

아래와 같은 프로토콜이 있다고 가정해보자.

protocol SomeType: class {
  associatedtype Model
  
  func doSomethingWithModel(model: Model)
}

그리고 아래와 같은 메서드를 구현했다고 해보자.

func doSomethingWithTypes(types: [SomeType]) {
  for type in types {
    type.doSomethingWithModel(//여기에 어떤 타입의 argument가 전달됐는지 알아야 하는데, 그것이 모호하다)
  }
}

Swift는 이런 모호함을 좋아하지 않기 때문에 associated type을 가진 프로토콜로 변수를 선언하는 것을 허용하지 않는다.