SwifT:获取通用协议作为参数的通用函数不起作用

SwifT: Generic function that gets generic protocol as parameter not working

假设我有这个简单的通用协议

protocol FooProtocol {
    associatedtype Element: CustomStringConvertible

    func echo(element: Element) -> Element
}

以及实现它的这个简单的通用结构

struct FooStruct<Element: CustomStringConvertible>: FooProtocol {
    func echo(element: Element) -> Element {
        return element
    }
}

最后,我有以下功能:

func callEcho<T: FooProtocol>(container: T) {
    container.echo(element: "some string")
}

这导致 error: cannot invoke 'echo' with an argument list of type '(element: String)'

解决办法是把函数改成

func callEcho<T: FooProtocol>(container: T) where T.Element == String {
    container.echo(element: "some string")
}

我的问题是:为什么需要 where T.Element == String 约束?编译器知道 T 是实现 FooProtocol 的某个实体,协议只要求 Element 实现 CustomStringConvertible,而我的字符串文字就是这样做的。那么为什么没有约束它就不能工作呢?

谢谢!

我不确定你为什么说 "the protocol only demands that Element implements CustomStringConvertible." 协议要求 echo 接受和 return its 元素,这可能或可能不是字符串。例如,我可以实现这个符合类型:

struct AnotherFoo: FooProtocol {
    func echo(element: Int) -> Int { fatalError() }
}

那么如果我调用:

会发生什么
callEcho(container: AnotherFoo())

这会尝试将字符串文字传递给需要 Int 的函数。

container 的类型为 T,因此 container.echo(element:) 需要一个类型为 T.Element 的参数——而这不一定是字符串。

如果打算将字符串文字传递给方法,则 T.Element 必须采用 ExpressibleByStringLiteral,而不是 CustomStringConvertible:

protocol FooProtocol {
    associatedtype Element: ExpressibleByStringLiteral

    func echo(element: Element) -> Element
}

现在编译:

func callEcho<T: FooProtocol>(container: T) {
    _ = container.echo(element: "some string")
}