[Swift] Opaque Result Type을 사용하여 Genesic형 매개변수를 숨깁니다!

13098 단어 Swifttech

추기


더 좋은 방법이 생겼다.
https://zenn.dev/en3_hcl/articles/377e9d15e323be

정황


프로그램 라이브러리를 제작할 때 자주 다음과 같은 상황을 만날 수 있다.
public protocol MyPublicProtocol: Equatable {
    init()
    var number: Int { get }
}

// 何らかの値
public struct MyValue: MyPublicProtocol {
    public init() {}
    public let number: Int = 46
}

public struct MyPublicStruct<T: MyPublicProtocol> {
    private var value: T

    func isEqual(to value: T) -> Bool {
        return value == self.value
    }
}
MyPublicStruct는 사용자도 사용하기를 원하는 구조체다.따라서 이것public은 문제없다.
문제는MyValue입니다.이는 사실 이용자가'몰라도 된다'는 것으로, 이용자가 이용하지 못하게 하려는 것이다.
그러나 사용자들이 이걸 사용할 수 있도록 하기 위해서는 다음과 같은 명칭이 없어서는 안 된다.본의는 아니지만 MyValuepublic로 바꿀 필요가 있다.
// 別のモジュールで初期化する処理
let instance = MyPublicStruct<MyValue>()
어떤 상황입니까?
예를 들어 JapaneseKeyboardInputManager<T: InputStyleData>가'일본어 키보드 입력 관리 유형'이라고 가정하고 InputStyleData를 따르는 것은
  • 또는 입력한 데이터KanaInputStyleData
  • 로마자 입력 시 데이터RomanInputStyleData
  • 의 경우KanaInputStyleDataRomanInputStyleData 모두 실크로 돼 있어 외부에 공개하고 싶지 않다.근데 그걸 관리하는'일본어 키보드 입력 관리 유형'은 공개하고 싶어요.

    하고 싶은 일


    어떻게든 숨기고 싶어MyValue.숨겨진다면 Opaque Result Type입니다.그걸로 하면 괜찮을 것 같은데.
    가장 좋은 방법은 다음과 같다typealias.그러나 이런 문장은 없고, 지금도 이런 문장이 이미 예정되어 있다는 것을 들어 본 적이 없다.
    // 利用者にはMyPublicStructForUserがMyPublicStruct<some MyPublicProtocol>に見えて欲しい
    typealias MyPublicStructForUser = MyPublicStruct<MyValue> as MyPublicStruct<some MyPublicProtocol>
    
    이건 못해도 아래처럼 쓰면 괜찮아.이 방법은 외부MyPublicStructFactory.instance()를 통해 초기화되고MyValue 숨길 수 있습니다.
    public enum MyPublicStructFactory {
        // 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
        static func instance() -> MyPublicStruct<some MyPublicProtocol> {
            return MyPublicStruct<MyValue>(value: .init())
        }
    }
    
    그런데 이것도 불가능해요.오류 메시지와 같이 반환 값에는 유형 매개변수로 Opaque Result Type을 사용할 수 없습니다.
    이것은 Opaque Result Type이 현재 적용하고 있는 제한이며 향후 실현될 수 있습니다.위쪽typealias보다 희망이 있는 것 같아요.근데 그 전에 꼭 기다려야 돼요?

    해결하다


    그럴 필요 없어.실현하는 방법을 조금만 바꾸면 이것이 가능하다.
    public protocol MyPublicStructProtocol {
        associatedtype T: MyPublicProtocol
        func isEqual(to value: T) -> Bool
    }
    
    private struct MyPublicStruct<T: MyPublicProtocol>: MyPublicStructProtocol {
        private var value: T
    
        func isEqual(to value: T) -> Bool {
            return value == self.value
        }
    }
    
    private struct MyValue: MyPublicProtocol {
        let number: Int = 46
    }
    
    public enum MyPublicStructFactory {
        static func instance() -> some MyPublicStructProtocol {
            return MyPublicStruct<MyValue>(value: .init())
        }
    }
    
    최종 사용자가 얻은 것은 some MyPublicStructProtocol의 유형이다.이렇게 되면 아무것도 하지 않은 것처럼 보이지만 MyPublicStructProtocol public에 올려놓을 것을 약속했기 때문에 이 사용에는 아무런 장애가 없다.또 대외적으로 공개된 것은 오파케형에 불과하기 때문에MyPublicStructMyValue 모두 공개할 필요가 없으며value에 대해서는 원래 진입protocol에 대한 요구가 없기 때문에 외부에서 방문할 수 없다.
    실제로 사용자들은 어떻게 운영하고 있을까.언뜻 보기에는 불가사의한 일이지만 곤란한 것은 없다.
    let instance = MyPublicStructFactory.instance()
    let isEqual = instance.isEqual(to: .init())
    let isEqualFunction = instance.isEqual
    
    instancesome MyPublicStructProtocol 유형의 값이다.isEqual 문제 없는 호칭.
    흥미로운 것은 isEqual라고 부르려면 점부호로만 불러야 한다.init().T 숨겨져 있기 때문에 이용자는 그것이 어떤 유형인지 알 수 없다.그럼에도 불구하고 MyPublicProtocol 알파벳 발생기의 존재를 약속했기 때문에 점 기호를 통해서만 초기화할 수 있다.
    시험해 보면 isEqualFunction 그 뜻을 알 수 있다.이것은 ((some MyPublicStructProtocol).T) -> Bool 유형의 함수다.
    위에서 확인한 바와 같이 숨겨진 파라미터의 제니릭형을 순조롭게 얻을 수 있습니다.다만, 당초 목적MyPublicStruct이 공개됐으면 좋았을 텐데 이 방법이 숨겨져'과도한 실복 은폐'상황으로 변해 다소 달갑지 않다.
    더 좋은 방법이 있다면 꼭 알려주세요!

    좋은 웹페이지 즐겨찾기