Неожиданная ошибка компилятора при попытке использовать универсальный тип при закрытии

#swift #generics

#swift #общие

Вопрос:

Учитывая протокол без каких-либо проблем вообще:

 protocol NonFunkyProtocol {}
  

И протокол, в котором происходят серьезные сбои:

 protocol FunkyProtocol {
    func funky<T: NonFunkyProtocol>(_ closure: (T) -> Void)
}
  

Затем, учитывая эту структуру:

 struct WeeStruct: FunkyProtocol {
    let weeProp: NonFunkyProtocol
    
    func funky<T>(_ closure: (T) -> Void) where T: NonFunkyProtocol {
        closure(weeProp)
    }
}
  

Я ожидал бы, что это будет скомпилировано, поскольку тип параметра, ожидаемый в closure , равен T, где T соответствует NonFunkyProtocol и weeProp имеет тип NonFunkyProtocol.

Вместо этого я вижу эту ошибку:

ошибка

У меня, скорее всего, где-то в моих знаниях общих терминов есть пробел, где я ошибаюсь?

Ответ №1:

Проблема в том, что T в данном случае это «некоторый тип, который соответствует NonFunkyProtocol «. weeProp также «что-то, что соответствует NonFunkyProtocol «, но ничто не говорит о том, что weeProp имеет тип T .

Рассмотрим следующий случай:

 extension Int: NonFunkyProtocol {}
extension String: NonFunkyProtocol {}
  

Оба Int и String соответствуют.

Теперь я создаю WeeStruct со строкой:

 let wee = WeeStruct(weeProp: "")
  

И я вызываю funky функцию, для которой требуется значение Int (поскольку Int соответствует типу, это может быть T ):

 wee.funky { (int: Int) -> Void in print(int   1) }
  

Таким образом, это передалось бы "" к закрытию. Как это может работать?

Таким образом, вам либо нужно запросить дескриптор закрытия any NonFunkyProtocol (я сильно подозреваю, что это то, что вы имеете в виду):

 func funky(_ closure: (NonFunkyProtocol) -> Void)
  

Или вам нужно закрепить weeProp до T , создав T associatedtype:

 protocol FunkyProtocol {
    associatedtype T: NonFunkyProtocol
    func funky(_ closure: (T) -> Void)
}

struct WeeStruct<T:NonFunkyProtocol>: FunkyProtocol {
    let weeProp: T

    func funky(_ closure: (T) -> Void) {
        closure(weeProp)
    }
}
  

Однако я был бы очень осторожен, прежде чем добавлять associatedtype . Это полностью меняет природу FunkyProtocol.

Если FunkyProtocol действительно является только этим требованием, вам также следует спросить, что он решает, а не просто функцию. Зачем передавать WeeStruct весь свой протокольный багаж, когда вы могли бы просто использовать wee.funky функцию напрямую? Есть ли расширение протокола в FunkyProtocol? Если вы не можете написать универсальные алгоритмы для FunkyProtocol, вероятно, это не должен быть протокол.

Комментарии:

1. Момент лампочки! Большое спасибо, в этом конкретном примере вы правы, это не должен быть протокол, но это был просто очень дистиллированный пример, чтобы попытаться обдумать проблему, с которой я столкнулся в одном из моих проектов.