如何在 Swift 中创建一个通用函数,它将拒绝给定的参数,除非它是可选的?

How to create a generic function in Swift that will reject the given parameter unless it is an Optional?

这个问题是我之前问题的后续问题:

Please read the referred question for you to get a better idea of the constraints at hand.

我在 Swift 中创建了一个通用函数,它将拒绝其参数,除非这样的参数是 Optional。我创建的功能完全可以工作并且可以满足我的要求。

意思是,任何对 onlyCallableByAnOptable(...) 的调用,即使在 if let 内部,也会由于不符合协议而产生错误,完全符合要求

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

我唯一的问题是:有更简单的解决方案吗?

To make it clear: func onlyCallableWithAnOptinalParameter<T>(:T)->T needs to work when called in an if let statement like func test() does.

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

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 optionalOptable() -> OptableType? {
        return self
    }
}


class TestOptable {
    static func test()
    {
        let c = UIColor.blue
        let s = "hi"
        let i = Int(10)
        let oi: Int? = 10

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

您需要将传递给 onlyCallableByAnOptionalvalue 参数设为可选。同样,在该函数的 return 语句中,您还需要选择展开 value 以便它可以执行 optionalOptable 函数。

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

你可能想给这个协议起一个更好的名字,但我预计它不会有任何问题,除非你正在制作自己的 ExpressibleByNilLiteral 不换行的类型。

protocol ExpressibleByNilLiteral: Swift.ExpressibleByNilLiteral {
  associatedtype Wrapped
}

extension Optional: ExpressibleByNilLiteral { }

func onlyCallableByAnOptional<Optional: ExpressibleByNilLiteral>(_ optional: Optional) -> Optional.Wrapped? {
  optional as? Optional.Wrapped
}

建议:使用初始化程序。 (缺点是参数标签是消除歧义所必需的,但我个人喜欢这种明确性,因为这种情况很奇怪。即 Swift 可以很容易地强制执行 不是 可选,反之则不然。)

extension Optional: ExpressibleByNilLiteral {
  init<Optional: ExpressibleByNilLiteral>(optional: Optional) where Optional.Wrapped == Wrapped {
    self = optional as? Wrapped
  }
}

+

if let v = Optional(optional: i) {  // ERROR, as was desired.
  print("integer \(v)")
}
if let v = Optional(optional: oi) {  // OK, as expected.
  print("optional integer \(v)")
}

以下是@Jessy的解决方案,基本上是我的解决方案的简化。

干得好,杰西。

我决定在这里用different/simpler重写它,希望通用类型协议"confusing"的名称更少,为了更易读,让新手容易混淆,也更接近我问题中使用的名称。

If anyone happens to know of an even more elegant approach, you are very welcome to post it.

protocol Optable {
    associatedtype Wrapped
}

extension Optional: Optable { }

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

或者,如果您碰巧更喜欢 Jessy 的使用初始化程序的解决方案,这里是重命名的版本:

protocol Optable {
    associatedtype Wrapped
}

extension Optional: Optable {
    init<T: Optable>(optional o: T) where T.Wrapped == Wrapped {
        self = o as? Wrapped
    }
}