Как получить доступ к функции, объявленной в протоколе по умолчанию и использующей связанный тип из другого протокола? - PullRequest
4 голосов
/ 06 ноября 2019

Вчера я задал вопрос . Это попытка сформулировать вопрос более четко.

КОНТЕКСТ:

У меня есть семейство 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())

1 Ответ

0 голосов
/ 07 ноября 2019

Оказывается, что только VectorType вызывал проблему, и ее можно решить, добавив это ограничение !!

extension LineProtocol where Self.VectorType == Self.PointType.VectorType {
    // Constraint passes VectorType thru to the PointProtocol
    public func endOfLineSegment() -> PointType {
        return (anchor + direction)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...