Вчера я задал вопрос . Это попытка сформулировать вопрос более четко.
КОНТЕКСТ:
У меня есть семейство struct
s (называемое Vector2D, Vector3D и т. Д.), Которые представляют векторы в различных измерениях. Их реализации очень похожи, но поскольку наследование недоступно для struct
s, я использую protocol
(VectorProtocol), который позволяет мне писать функции-члены по умолчанию как protocol extensions
вместо того, чтобы повторять их для каждого struct
.
Аналогично, у меня есть семейство struct
s (называемое Point2D, Point3D и т. Д.), Которые представляют точки в различных измерениях. Их реализации также очень похожи, поэтому я использую другой протокол (PointProtocol) по той же причине.
В каждом случае протоколы используют associatedtype
s для идентификации конкретного типа экземпляра struct
.
Иногда PointxD и VectorxD взаимодействуют, поэтому PointProtocol
также имеет associatedtype
для идентификации конкретного VectorxD, с которым он должен работать, и VectorProtocol
имеет associatedtype
для идентификации конкретного PointxD, с которым он должен работать.
Пока что все работает нормально, включая взаимодействие между точками и векторами. Например, разница между двумя точками - это вектор, а сумма точки - это вектор, а вектор - это точка, и я могу один раз определить операторы для этих случаев для каждого измерения следующим образом:
public protocol PointProtocol {
associatedtype VectorType: VectorProtocol
/* etc. */
}
extension PointProtocol {
public static func +(lhs: Self, rhs: VectorType) -> Self { /*…*/ }
public static func -(lhs: Self, rhs: Self) -> VectorType { /*…*/ }
}
ПРОБЛЕМА:
Теперь я хочу добавить еще одно семейство struct
s (называемое Line2D, Line3D и т. Д.), Которые представляют линии в различных измерениях и которые взаимодействуют с точками и векторами. Я думал, что делаю больше того же самого, но есть небольшая разница. Линии состоят из точек и векторов.
public protocol LineProtocol {
associatedtype PointType: PointProtocol
associatedtype VectorType: VectorProtocol
var anchor: PointType { get set }
var direction: VectorType { get set }
}
public struct Line2D: LineProtocol {
public typealias PointType = Point2D
public typealias VectorType = Vector2D
var anchor: PointType
var direction: VectorType
public init(anchor: Point2D, direction:Vector2D) { … }
}
Это не вызывает проблем, когда речь идет о построении, например, Line2D с использованием Point2D и Vector2D. Однако компилятор ошибается, когда дело доходит до этого объявления по умолчанию:
extension LineProtocol {
public func endOfLineSegment() -> PointType {
return (anchor + direction) // Compiler ERROR
// Binary operator '+' cannot be applied to operands of type
// 'Self.PointType' and 'Self.VectorType'
}
}
Похоже, что компилятор не может найти объявление оператора public static func +(lhs: PointType, rhs: VectorType) -> PointType
, хотя anchor
явно имеет тип PointProtocol
, а направлениеясно типа VectorProtocol
. Поэтому я думаю, что endOfLineSegment()
знает, что привязкой и направлением являются PointType и VectorType соответственно, что означает, что он должен знать, что они также являются PointProtocol и Vector Protocol, но он не знает, как установить два associatedtype
s для этих протоколов.
Кто-нибудь знает, как это исправить? (Без необходимости реализовывать функцию отдельно в каждой struct
декларации, которая работает)
Ниже приведен минимальный код, достаточный для генерации ошибки на игровой площадке
public protocol PointProtocol {
associatedtype PointType: PointProtocol
associatedtype VectorType: VectorProtocol
var elements: [Float] { get set }
}
extension PointProtocol {
public static func +(lhs: Self, rhs:VectorType) -> Self {
var translate = lhs
for i in 0..<2 { translate.elements[i] += rhs.elements[i] }
return translate
}
}
public protocol VectorProtocol {
associatedtype VectorType: VectorProtocol
var elements: [Float] { get set }
}
public struct Point: PointProtocol {
public typealias PointType = Point
public typealias VectorType = Vector
public var elements = [Float](repeating: 0.0, count: 2)
public init(_ x: Float,_ y: Float) {
self.elements = [x,y]
}
}
public struct Vector: VectorProtocol {
public typealias VectorType = Vector
public static let dimension: Int = 2
public var elements = [Float](repeating:Float(0.0), count: 2)
public init(_ x: Float,_ y: Float) {
self.elements = [x,y]
}
}
public protocol LineProtocol {
associatedtype PointType: PointProtocol
associatedtype VectorType: VectorProtocol
var anchor: PointType { get set }
var direction: VectorType { get set }
}
extension LineProtocol {
public func foo() -> PointType {
return (anchor + direction)
}
}
public struct Line: LineProtocol {
public typealias PointType = Point
public typealias VectorType = Vector
public var anchor: PointType
public var direction: VectorType
public init(anchor: Point, direction: Vector) {
self.anchor = anchor
self.direction = direction
}
public func bar() -> Point {
return (anchor + direction)
}
}
let line = Line(anchor: Point(3, 4), direction: Vector(5, 1))
print(line.bar())
//print(line.foo())