swift - 如何访问在协议中声明为默认值的函数并使用来自另一个协议的关联类型?
问题描述
昨天我问了一个问题。这是试图更清楚地说明问题。
上下文:
我有一个struct
s 族(称为 Vector2D、Vector3D 等),它们表示各种维度的向量。它们的实现非常相似,但由于struct
s 无法继承,因此我使用protocol
(VectorProtocol) 来编写默认成员函数,protocol extensions
而不是为 each 重复它们struct
。
同样,我有一个struct
s 族(称为 Point2D、Point3D 等),它们表示不同维度的点。它们的实现也非常相似,所以出于同样的原因我使用了另一个协议(PointProtocol)。
在每种情况下,协议都使用associatedtype
s 来标识特定的struct
实例类型。
有时 PointxDs 和 VectorxDs 交互,因此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) { … }
}
例如,在使用 Point2D 和 Vector2D 构建 Line2D 时,这不会造成任何问题。但是,当涉及到这个默认声明时,编译器会犹豫:
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
它显然是 type PointProtocol
并且 direction 显然是 type VectorProtocol
。所以我认为endOfLineSegment()
知道anchor和direction分别是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())
解决方案
事实证明,这只是导致问题的 VectorType,可以通过添加此约束来解决!
extension LineProtocol where Self.VectorType == Self.PointType.VectorType {
// Constraint passes VectorType thru to the PointProtocol
public func endOfLineSegment() -> PointType {
return (anchor + direction)
}
}
推荐阅读
- html - 图像未调整大小
- php - jQuery 使用链接属性模拟在页面加载时单击链接
- html - CSS Navbar 项目不按顺序排列?
- python - 全屏tkinter画布python
- node.js - 在 Node 14 中使用 sequelize-cli,类型为:module
- android - appbundle 和 split 在启动 android flutter 时崩溃
- python - 创建一个动态变量并在 python (tkinter) 中进一步使用它
- javascript - 如何正确设置带有 React Context 的 Axios 拦截器?
- reactjs - 如何在多个视图中处理突变后的缓存更新?
- r - 如何将一些数据附加到现有的 xlsx 工作表 R 中?