Как создать обобщенную c функцию в Swift, которая будет отклонять данный параметр, если он не является необязательным? - PullRequest
0 голосов
/ 05 марта 2020

Этот вопрос является продолжением моего предыдущего вопроса: Я ожидал, что система сообщит о несоответствии протокола, но это не так! Почему?

Пожалуйста, прочитайте приведенный вопрос, чтобы получить лучшее представление об имеющихся ограничениях.

Я создал обобщенную c функцию в Swift, который отклонит свой параметр, если такой параметр не равен Optional. Созданная мною функция полностью работает и выполняет то, что я хочу.

Это означает, что любые вызовы onlyCallableByAnOptable(...), даже внутри if let, приведут к ошибке из-за непротокольного соответствия, точно так, как требуется .

Ошибки типа: Argument type 'UIColor' does not conform to expected type 'Optable'

Мой единственный вопрос: Есть ли более простое решение?

Чтобы было понятно: func onlyCallableWithAnOptinalParameter<T>(:T)->T должен работать при вызове в операторе if let, как func test() делает.

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

Ответы [ 3 ]

1 голос
/ 06 марта 2020

Возможно, вы захотите дать этому протоколу более подходящее имя, но я не предвижу никаких проблем с ним как есть, если только вы не создаете свои 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)")
}
0 голосов
/ 06 марта 2020

Ниже приведено решение @ Jessy's , которое по сути является упрощением моего решения.

Молодец, Джесси.

Я решил переписать его здесь с другими / более простыми, надеюсь, менее "запутанными" именами для generi c типа и протокол , чтобы сделать его более читабельным, пусть он склонен к замешательству со стороны новичков, а также больше похож на имена, используемые в моем вопросе.

Если кто-то узнает о еще большем элегантный подход, вы можете опубликовать его.

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
    }
}
0 голосов
/ 06 марта 2020

Вам необходимо сделать необязательным параметр value, который вы передаете в onlyCallableByAnOptional. Аналогично, в операторе возврата этой функции вам также необходимо будет дополнительно развернуть value, чтобы он мог выполнить функцию optionalOptable.

func onlyCallableByAnOptable<T>( _ value: T?) -> T.OptableType? where T: Optable {
    return value?.optionalOptable()
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...