0

This question is a follow up to my earlier question: I expected the system to report non protocol conformance, but it does not! Why?

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

I created a generic function in Swift that will reject its parameter unless such a parameter is Optional. The function I created fully works and does what I desire.

Meaning, any calls to onlyCallableByAnOptable(...), even inside an if let, will yield error due to non-protocol conformance, exactly as desired.

Errors like: Argument type 'UIColor' does not conform to expected type 'Optable'

My only question is: Is there a simpler solution?

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)") 
        }
    }
}
4

3 回答 3

1

你可能想给这个协议起一个更好的名字,但我不认为它有任何问题,除非你正在制作自己的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)")
}
于 2020-03-06T00:45:14.620 回答
0

您需要将value传入的参数设为onlyCallableByAnOptional可选。同样,在该函数的返回语句中,您还需要选择性地解包value,以便它可以执行该optionalOptable函数。

func onlyCallableByAnOptable<T>( _ value: T?) -> T.OptableType? where T: Optable {
    return value?.optionalOptable()
}
于 2020-03-06T00:20:54.460 回答
0

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

干得好杰西。

我决定在这里用不同/更简单的名称重写它,希望对泛型类型协议的“混淆”名称更少,使其更具可读性,让新手容易混淆,并且与我的问题中使用的名称更相似.

如果有人碰巧知道更优雅的方法,非常欢迎您发布它。

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
    }
}
于 2020-03-06T09:35:24.760 回答