Почему компилятор не видит код по умолчанию в протоколе? - PullRequest
2 голосов
/ 05 ноября 2019

Редактировать: я переформулировал и, надеюсь, прояснил этот вопрос по здесь . Теперь я добавил решение.

Я определил функцию (см. foo() в прикрепленном примере) в качестве функции по умолчанию для struct s, приняв мой protocol. Он применяет оператор +, определенный для двух других переменных, которые сами принимают другие protocols, а + определен в одном из этих протоколов. Переменные набираются с использованием associatedtype s. Я получаю сообщение:

Двоичный оператор «+» не может быть применен к операндам типа «Self.PointType» и «Self.VectorType»

Если я реализуюФункция внутри моего struct (см. bar () в приложении) работает, поэтому я уверен, что мой оператор + работает. Мой пример сведен к минимуму, необходимому для работы на детской площадке. Просто удалите комментарии в LineProtocol extension, чтобы получить ошибку. Мне кажется, что Self.PointType - это Point, а Self.VectorType - это Vector.

Для ясности: причина, по которой я использовал associatedtype s, заключается в том, что многие разные struct s принимаюткаждый из трех протоколов в примере, поэтому я не могу назвать их напрямую

public protocol 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())

Решение, адаптированное по предложению @ Honey: замените расширение на:

extension LineProtocol where Self.VectorType == Self.PointType.VectorType {
   public func foo() -> PointType {
      // Constraint passes VectorType thru to the PointProtocol
      return (anchor + direction)
   }
}

1 Ответ

1 голос
/ 05 ноября 2019

Я знаю, в чем проблема. Не уверен, что мое решение - лучший ответ.

Проблема в том, что оба ваших ассоциированных типа сами имеют ассоциированные типы.

Таким образом, в расширении компилятор Swift не может определить тип связанных типов - если вы его не ограничите.

Как и do:

extension LineProtocol where Self.VectorType == Vector, Self.PointType == Point {
    public func foo() -> Self.PointType {
      return (anchor + direction)
   }
}

Ваш код работает для вашего конкретного типа Line, потому что оба ваших ассоциированных типа имеют свои требования, т.е. мог бы избавиться от явного соответствия вашим требованиям к ассоциированному типу и позволить компилятору определить 1 соответствие вашим требованиям к ассоциированным типам и написать свой тип Line следующим образом:

public struct Line: LineProtocol {

   public var anchor: Point
   public var direction: Vector

   public init(anchor: Point, direction: Vector) {
      self.anchor = anchor
      self.direction = direction
   }

   public func bar() -> Point {
      return (anchor + direction)
   }
}

1: Обобщения - связанные типы

Благодаря выводу типа Swift вам фактически не нужно объявлять конкретный Item Int как часть определения IntStack. Поскольку IntStack соответствует всем требованиям протокола Container, Swift может вывести соответствующий Item для использования, просто взглянув на тип параметра элемента метода append (_ :) и тип возвращаемого значения нижнего индекса. Действительно, если вы удалите строку typealias Item = Int из приведенного выше кода, все будет работать, потому что ясно, какой тип следует использовать для Item.

...