【问题标题】:Swift Generic Constraint cannot convert valueSwift Generic Constraint 无法转换值
【发布时间】:2017-11-11 12:35:24
【问题描述】:

在我的实际代码中,MyFoo 对象做了其他一些我不想分享的实现细节。因此,我试图将它隐藏在我的 Foo 协议后面,但是,我找不到必要的 where 子句来正确映射类型:

protocol Foo {
    associatedtype Bar
    func process(bar:Bar)
}

class MyFoo<T>: Foo {
    func process(bar: T) {}
}

class Buzz<U> {
    private let myFoo = MyFoo<U>()

    init<BarProcessor:Foo>(block:(BarProcessor)->Void) where BarProcessor.Bar == U {
        block(myFoo) // error: '(@lvalue MyFoo<U>) -> Void' is not convertible to '(BarProcessor) -> Void'
    }
}

或者我在这里遗漏了一个更基本的概念来说明为什么这不起作用?

【问题讨论】:

    标签: swift generics protocols


    【解决方案1】:

    这并不意味着你认为它意味着什么:

    init<BarProcessor:Foo>(block:(BarProcessor)->Void) where BarProcessor.Bar == U 
    

    这是试图将BarProcessor 视为已专门化的协议。但事实并非如此。 BarProcessor 这里是具体类型。因此,您传递了一个接受某些 特定 (但通用)类型的块。然后您尝试将MyFoo 传递给它,它可能不是那种特定类型。

    当您发现自己以这种方式混合协议和泛型时,您可能过度使用了协议。摆脱Foo。协议不是隐藏实现细节的方法。隐藏实现细节的工具是访问控制(privateinternal)。

    如果你想完全隐藏类型,那就是类型橡皮擦,而不是协议。例如(将事物重命名为没有“Foo”和“Bar”的含义):

    private struct MyProcessor<T> {
        func process(element: T) {}
    }
    
    // Type-erases MyProcessor
    struct Processor<T> {
        fileprivate let processor: MyProcessor<T>
        func process(element: T) { processor.process(element: element) }
    }
    
    class Machine<U> {
        private let myProcessor = MyProcessor<U>()
    
        init(block: (Processor<U>)->Void) {
            block(Processor(processor: myProcessor))
        }
    }
    

    或者,如果您有多个处理器的内部实现要设为私有,您可以使用私有协议,但关键是外部世界只能看到类型擦除器,而不是 PAT。

    private protocol Processing {
        associatedtype Element
        func process(element: Element)
    }
    
    private struct MyProcessor<T>: Processing {
        func process(element: T) {}
    }
    
    struct Processor<T> {
        private let _process: (T) -> ()
        fileprivate init<P: Processing>(_ processor: P) where P.Element == T {
            _process = processor.process
        }
        func process(element: T) { _process(element) }
    }
    
    class Machine<U> {
        private let myProcessor = MyProcessor<U>()
    
        init(block: (Processor<U>)->Void) {
            block(Processor(myProcessor))
        }
    }
    

    【讨论】:

    • 谢谢 Rob,这是有道理的。我仍然对我的代码在做什么有点模糊,但我明白了要点;但是使用类型擦除可以实现我一直尝试的相同目标。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多