Swift Generic Constraint无法转换值

时间:2017-11-11 12:35:24

标签: swift generics protocols

在我的实际代码中,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'
    }
}

还是有一个更基本的概念,我在这里错过了为什么这不起作用?

1 个答案:

答案 0 :(得分: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))
    }
}