Скажем, я хочу представить некоторые функциональные возможности в качестве публичного API, но часть реализации относится к внутренним протоколам:
public protocol P {
var foo: Int { get }
}
internal protocol Q {
init(from: [String])
}
public struct S: P, Q {
public var foo: Int = 0
public init() {}
internal init(from: [String]) {
precondition(from.count > 0)
self.foo = Int(from[0])!
}
}
Это может быть некоторый объект данных, который может сконструировать только мой собственный модуль (из некоторого представления данных), но который пользователи модуля могут использовать для своих собственных целей.
Скажем, я хочу предоставить какой-то сервис, который принимает такое значение и возвращает новый такой же тип:
public class ProviderOfThings {
public func map<T: P>(before: T) -> T {
return T(from: [String(before.foo + 1)])
}
}
Это не компилируется; в T
нет подходящего инициализатора.
Как я могу назвать конструктор, которого я (внутренне) знаю, чтобы быть там в общем виде?
2 ответа
Мы можем привести значение к внутреннему типу протокола и получить доступ к инициализатору оттуда:
public static func map<T: P>(before: T) -> T {
return type(of: before as! Q).init(
from: [String(before.foo + 1)]
) as! T
}
Мы можем даже обойтись без значения типа (спасибо, Хэмиш!):
public static func make<T: P>() -> T {
return (T.self as! Q.Type).init(
from: ["0"]
) as! T
}
Если есть (или могут быть) реализации P
, которые также не соответствуют Q
, приведение к Q
должно быть защищено, очевидно:
guard let beforeQ = before as? Q else { ... }
// or
guard before is Q else { ... }
// respectively
guard T.self is Q.Type else { ... }
Другое приведение as! T
безопасно: в конце концов мы вызываем инициализатор T
. Поэтому, если оно охраняется, это решение не может вызвать ошибки времени выполнения.
Полный код с примерами можно найти здесь.
Я думаю, что лучшим способом было бы объявить map
следующим образом:
public func map<T: P & Q>(before: T) -> T
И сделайте Q
общедоступным, возможно, добавив к нему префикс _
, если он не должен использоваться внешним кодом.
Похожие вопросы
Новые вопросы
swift
Swift — это язык программирования общего назначения, разработанный Apple Inc., впервые выпущенный в 2014 году для своих платформ и Linux. Swift имеет открытый исходный код. Используйте тег только для вопросов о языковых функциях или необходимости кода в Swift. Используйте теги [ios], [ipados], [macos], [watch-os], [tvos], [swiftui], [cocoa-touch] и [cocoa] для (не зависящих от языка) вопросов о платформах или рамки.