我希望系统报告不符合协议,但事实并非如此!为什么?

I expected the system to report non protocol conformance, but it does not! Why?

我正在使用 Xcode 版本 11.3.1 (11C504)

I am trying to create a generic function in Swift that will reject its parameter unless such a parameter is Optional.

在下面的代码中,我期望系统在对onlyCallableByAnOptable()的所有调用中报告错误test(),因为其中 none 提供了一个 可选 值作为参数。

但是,如果我删除符合 Optable!

Optional 扩展,系统只会报告不符合协议

对我来说,这意味着系统将 任何和所有 值视为 Optional,无论如何!

我是不是做错了什么?

(顺便说一下,下面的代码曾经在 Swift 的早期版本中按预期工作。我最近才发现它停止工作了,因为它让一个非-Optional通过。)

protocol Optable {
    func opt()
}

func onlyCallableByAnOptable<T>( _ value: T) -> T where T: Optable {
    return value
}

// Comment the following line to get the errors 
extension Optional: Optable { func opt() {} }


class TestOptable {
    static func test() 
    {
        let c = UIColor.blue
        let s = "hi"
        let i = Int(1)

        if let o = onlyCallableByAnOptable(c) { print("color \(o)") }
        //^ expected ERROR: Argument type 'UIColor' does not conform to expected type 'Optable'

        if let o = onlyCallableByAnOptable(s) { print("string \(o)") }
        //^ expected ERROR: Argument type 'String' does not conform to expected type 'Optable'

        if let o = onlyCallableByAnOptable(i) { print("integer \(o)") }
        //^ expected ERROR: Argument type 'Int' does not conform to expected type 'Optable'
    }
}

由于您已经使所有 Optional 都符合 Optable 并且您正在使用 if let 语法来解包调用 onlyCallableByAnOptable 的结果(这意味着 return 类型必须是某种 Optional,这意味着参数也必须是相同类型的 Optional 因为参数和 return 类型都是类型T 在您的通用方法中),Swift 正在推断作为 UIColor?String?Int? 传入的类型(将它们隐式包装在 Optionals) 而不是 UIColorStringInt.

我就是这个问题的发帖人

I was trying to create a generic function in Swift that would reject its parameter unless such parameter is an Optional.

正如 @TylerTheCompiler 指出的那样,使用我的原始实现(在问题中),Swift 推断类型 T(用于 onlyCallableByAnOptable()),基于调用的完整上下文,而不仅仅是作为参数提供的值的类型,因此推断 TOptional.

为了帮助那些可能正在尝试实现与我一样的目标的人,以下是我对我遇到的问题的解决方案。

由于不符合协议,所有对 onlyCallableByAnOptable(...) 的调用现在可以正确产生错误。

错误如:Argument type 'UIColor' does not conform to expected type 'Optable'

If anyone knows of a simpler solution, please do post it as an answer to: .

protocol Optable {
    associatedtype OptableType
    func optionalOptable() -> OptableType?
    func opt()
}

func onlyCallableByAnOptable<T>( _ value: T) -> T.OptableType? where T: Optable {
    return value.optionalOptable()
}


extension Optional: Optable {
    typealias OptableType = Wrapped //: Wrapped is the type of the element, as defined in Optional
    func opt() {}
    func optionalOptable() -> OptableType? {
        return self
    }
}


class TestOptable {
    static func test()
    {
        let c = UIColor.blue
        let s = "hi"
        let i = Int(1)

        if let o = onlyCallableByAnOptable(c) {  // ERROR, as was desired.
            print("color \(o)") 
        }
        if let o = onlyCallableByAnOptable(s) {  // ERROR, as was desired.
            print("string \(o)") 
        }
        if let o = onlyCallableByAnOptable(i) {  // ERROR, as was desired.
            print("integer \(o)") 
        }
    }
}