使用`Self`作为参数,使用方法键入擦除

时间:2016-10-01 17:41:39

标签: swift range protocols type-erasure

我正在尝试围绕RangeClosedRange进行类型删除,但我被卡住了,因为他们有一些方法以Self作为参数。

互联网上发现的所有类型的擦除样本都不涉及这种情况。

我想做一些不可能的事吗?

这是我的实施(简化):

protocol RangeType {
    associatedtype _Bound: Comparable

    func overlaps(_ other: Self) -> Bool
}

struct AnyRange<Bound: Comparable>: RangeType {
    typealias _Bound = Bound

    private let _overlaps: (AnyRange<Bound>) -> Bool

    init<R: RangeType>(_ range: R) where R._Bound == Bound {
        // Cannot assign value of type (R) -> Bool to type (AnyRange<...>) -> Bool
        self._overlaps = range.overlaps
    }

    func overlaps(_ other: AnyRange<Bound>) -> Bool {
        return _overlaps(other)
    }
}

extension Range: RangeType {
    typealias _Bound = Bound
}

extension ClosedRange: RangeType {
    typealias _Bound = Bound
}

1 个答案:

答案 0 :(得分:1)

在我提出该问题的解决方案之前,首先请注意,您尝试执行的操作可能未定义。协议RangeType确保为类型与实现该功能的类型相同的实例定义overlaps(_:)。您无法通过模仿AnyIterator来尝试擦除类型,因为AnyRange可以保证边界是相同的,但是实际的基础类型本身可能不是(协议)。

但是,这里解决了。如果愿意,可以添加一个特殊情况来处理两种不同类型之间的比较(尽管最好对false进行评估,如此处所述)

protocol RangeType {
    associatedtype _Bound: Comparable
    func overlaps(_ other: Self) -> Bool
}

struct RangeComparison<Range, Element>: Equatable where Range: RangeType, Range._Bound == Element {
    static func ==(lhs: RangeComparison<Range, Element>, rhs: RangeComparison<Range, Element>) -> Bool { lhs.range.overlaps(rhs.range) }
    var range: Range
}

struct AnyRange<Bound: Comparable>: RangeType {
    
    // MARK: RangeType
    
    typealias _Bound = Bound
    
    // Calls the closure of the `_overlaps` property which shields each type and allows self to be passed through
    func overlaps(_ other: AnyRange<Bound>) -> Bool { _overlaps.closure(other, self) }
    
    // Shielding structure. Allows us to compare to `AnyRange` instances
    private struct OverlapContainer<A, B> {
        private(set) var closure: (A, B) -> Bool
        init(closure: @escaping (A, B) -> Bool) { self.closure = closure }
    }
    private var _overlaps: OverlapContainer<AnyRange<Bound>, Self>
    
    // Holds reference to the actual range type. Note that if this is a class type, a strong reference will be created
    private let range: Any
    
    /**
        Represents this particular type. Should not be called elsewhere in the structure as the cast would fail if `RT != R`
        passed to the initiliazer
     
        NOTE: `RT` as the generic type is used instead of `R` to keep us aware of this fact
    */
    private nonmutating func rangeComparison<RT: RangeType>() -> RangeComparison<RT, Bound> { RangeComparison<RT, Bound>(range: range as! RT) }
    
    init<R: RangeType>(_ range: R) where R._Bound == Bound {
        self.range = range
        self._overlaps = .init { other, this in
            
            let thisComparison: RangeComparison<R, Bound> = this.rangeComparison()
   
            // If the two types are the same, the comparison can be made
            if type(of: other.range).self == R.self {
                let otherComparison: RangeComparison<R, Bound> = other.rangeComparison()
                return thisComparison == otherComparison
            }
            else { print("Not the same type"); return false } // Otherwise the comparison is invalid
        }
    }
}

extension Range: RangeType {
    typealias _Bound = Bound
}

extension ClosedRange: RangeType {
    typealias _Bound = Bound
}

// Examples

let range: Range<Int> = .init(5...8)
let rangeII: ClosedRange<Int> = 1...6

let any: AnyRange<Int> = .init(range)
let anyII: AnyRange<Int> = .init(rangeII)

print(any.overlaps(anyII)) // false.` Range` is not the same type as `ClosedRange`


let rangeIII: ClosedRange<Double> = 3.0...5.5
let rangeIV: ClosedRange<Double> = 1.0...4.0

let anyIII: AnyRange<Double> = .init(rangeIII)
let anyIV: AnyRange<Double> = .init(rangeIV)

print(anyIII.overlaps(anyIV)) // true. Both are 'ClosedRange<Double>' and actually overlap one another

这里有很多东西,所以让我解释一下每一件


struct RangeComparison<Range, Element>: Equatable where Range: RangeType, Range._Bound == Element {
    static func ==(lhs: RangeComparison<Range, Element>, rhs: RangeComparison<Range, Element>) -> Bool { lhs.range.overlaps(rhs.range) }
    var range: Range
}

此结构用于表示给定的AnyRange类型。如前所述,如果两个RangeType实例的类型不同,则不进行比较。这为确保这种情况提供了一种媒介,并使通过这种结构将两种AnyRange类型等同起来变得很方便。

rangeComparison<RT: RangeType>()方法使用传递给初始化程序的RangeType(R)的类型并强制转换range属性(设置为Any并分配给传递的实例初始化程序)创建一个RangeComparison实例。 range属性保留了实际的基础类型。


private struct OverlapContainer<A, B> {
    private(set) var closure: (A, B) -> Bool
    init(closure: @escaping (A, B) -> Bool) { self.closure = closure }
}
private var _overlaps: OverlapContainer<AnyRange<Bound>, Self>

此结构实际上允许我们通过AnyRange的{​​{1}}方法和闭包(间接)在两个overlaps(_:)实例之间进行比较。我们只需调用AnyRange属性的_overlaps属性,即可提供其他closure实例和该实例的副本。使用一个副本来确保闭包可以使用AnyRange而不必使用self,因为编译器会抱怨“转义闭包捕获了self参数的变体”(因此,{ {1}}有两种通用类型。

self

最后,我们检查两个比较是否具有相同的类型。如果您尝试将每个返回类型指定为OverlapContainer,它将进行编译,但如果每个比较的init<R: RangeType>(_ range: R) where R._Bound == Bound { self.range = range self._overlaps = .init { other, this in let thisComparison: RangeComparison<R, Bound> = this.rangeComparison() // If the two types are the same, the comparison can be made if type(of: other.range).self == R.self { let otherComparison: RangeComparison<R, Bound> = other.rangeComparison() return thisComparison == otherComparison } else { print("Not the same type"); return false } // Otherwise the comparison is invalid } } 属性的类型与从通用初始值设定项推断的类型RangeComparison<R, Bound>不同,则它将崩溃。您还“无法显式专门化泛型函数”,因此必须为range的结果指定类型。由于这两个原因,我们检查类型,然后检查它们是否重叠。