如何正确使用具有关联类型的协议来描述另一个对象中的 属性

how to properly use a protocol that has an associated type to describe a property in another object

我一直在创建一个 SwiftUI 组件,我希望这个组件有一个实现协议的 属性。具体用例是为图表绘制轴,它基于比例。

有几种具体的尺度实现可以将数据从输入域转换为输出范围。我开始使用的两个是线性标度,它从 Double 输入域转换为用 Double 表示的输出范围。另一个是基于 Date/Time 的比例尺,它从基于 Date 的输入域转换为由 Double.

表示的输出范围

规模是协议,我粗略地将其定义为:

public protocol Scale {
    associatedtype InputDomain: Comparable // input domain

    var isClamped: Bool { get }

    // input values
    var domain: ClosedRange<InputDomain> { get }

    // output values
    var range: ClosedRange<Double> { get }

    /// converts a value between the input "domain" and output "range"
    ///
    /// - Parameter inputValue: a value within the bounds of the ClosedRange for domain
    /// - Returns: a value within the bounds of the ClosedRange for range, or NaN if it maps outside the bounds
    func scale(_ inputValue: InputDomain) -> Double

    /// converts back from the output "range" to a value within the input "domain". The inverse of scale()
    ///
    /// - Parameter outputValue: a value within the bounds of the ClosedRange for range
    /// - Returns: a value within the bounds of the ClosedRange for domain, or NaN if it maps outside the bounds
    func invert(_ outputValue: Double) -> InputDomain

    /// returns an array of the locations within the ClosedRange of range to locate ticks for the scale
    ///
    /// - Parameter count: a number of ticks to display, defaulting to 10
    /// - Returns: an Array of the values within the ClosedRange of the input range
    func ticks(count: Int) -> [InputDomain]
}

LinearScale 和 TimeScale 结构符合协议,每个结构分别定义 typealias InputDomain = Doubletypealias InputDomain = Date

当我尝试使用此协议来描述更普遍地与 SwiftUI 组件一起使用的结构类型(比例)时,问题就出现了:

public struct AxisView: View {    
    let scale:  Scale

    public var body: some View { ... }
}

编译器报错: Protocol 'Scale' can only be used as a generic constraint because it has Self or associated type requirements

我不确定解决此问题的最佳方法是解决编译器 error/constraints。我应该做些什么来使 SwiftUI 组件成为通用的,还是应该不使用协议的关联类型?

或者是否有另一种方法来考虑使用协议和结构构建此代码以支持各种比例类型?

UPDATE:我得到了原始问题的答案,但对我来说还不是很清楚。我将通用定义添加到封闭类型(我的 Scale 实现)。

我不清楚的是为什么需要这个?在封闭结构上添加通用标记后,编译器错误消失了。我假设这是 swift 编译器可能采用的多个选项的地方,并告诉它 "yeah, I want this to be a generic" 是一条路径 - 其他可能的路径是什么?

我还注意到,即使它被定义为通用 class,我使用的特定 class 也经常被 swift 编译器推断出来。所以我不需要用通用语法完全指定类型。例如,我可以使用

LinearScale() 而不是 LinearScale<Double>(),它会推断出正确的泛型。这是预期的吗?

您也应该使您的视图通用:

public struct AxisView<ScaleType: Scale>: View {    
    let scale:  ScaleType    
    public var body: some View { ... }
}