Edit: I have restated and hopefully clarified this question over here. Now I've added the solution.
I've defined a function (see foo()
in attached example) as a default function for struct
s adopting my protocol
. It applies the +
operator defined in respect of two other variables which themselves adopt other protocols
and +
is defined in one of those protocols. The variables are typed using associatedtype
s.
I get the message:
Binary operator '+' cannot be applied to operands of type 'Self.PointType' and 'Self.VectorType'
If I implement the function inside my struct
(see bar() in attached) it works so I'm sure my + operator does work.
My example is pared down to the minimum needed to work in a playground. Just remove the comments in the LineProtocol extension
to get the error. It seems to me that Self.PointType
is a Point
and Self.VectorType
is a Vector
.
To be clear: The reason I used associatedtype
s is because many different struct
s adopt each of the three protocols in the example so I can't name them directly
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())
Solution adapted from @Honey's suggestion: replace extension with:
extension LineProtocol where Self.VectorType == Self.PointType.VectorType {
public func foo() -> PointType {
// Constraint passes VectorType thru to the PointProtocol
return (anchor + direction)
}
}
I know what the problem is. Not sure if my solution is the best answer.
The problem is that both your associatedtypes have associatedtypes themselves.
So in the extension, the Swift compiler can't figure out the type of the associatedtypes — unless you constrain it.
Like do:
Your code works for your concrete type
Line
, because both your associatedtypes have their requirements fulfilled ie:FWIW you could have got rid of the explicit conformance to your associatedtype requirements and let the compiler infer1 conformance to your associatedtypes requirements and write your
Line
type as such:1: Generics - Associated Types