简体   繁体   English

如何在 Swift 中实现通用约束?

[英]How to implement a generic constraint in Swift?

Imagine we have a generic enum like this:想象一下,我们有一个像这样的通用enum

enum GenericEnum<T> {
    case typed(T)
    case untyped(Error)
}

For the sake of easier access for some UI work, we have to implement an extension for this:为了更容易访问一些 UI 工作,我们必须为此实现一个扩展:

extension GenericEnum {
    
    var validValue: T? {
        if case .typed(let value) = self {
            return value
        } else {
            return nil
        }
    }
}

validValue returns the actual value or nil . validValue返回实际valuenil And we can use it like:我们可以像这样使用它:

let genericEnums = [GenericEnum.typed(1), GenericEnum.typed(2), GenericEnum.typed(3)]
let result = genericEnums.compactMap { $0.validValue }

So far, so good.到现在为止还挺好。

The issue问题

Now for more easy to call code, I want to move the usage method into an extension on the Array like:现在为了更容易调用代码,我想将使用方法移动到Array上的扩展中,例如:

extension Array where Element == GenericEnum<Int> { // `Int` should be generic here
    var customCompacted: [Int] { // `Int` should be generic here
        compactMap { $0.validValue }
    }
}

But I need Int to be generic , but all attempts raise an error like:但我需要Intgeneric ,但所有尝试都会引发如下错误:

Reference to generic type 'GenericEnum' requires arguments in <...>对泛型类型“GenericEnum”的引用需要 <...> 中的参数

And more different errors about generics and constraints.还有更多关于泛型和约束的不同错误。 How can I make this generic?我怎样才能使这个通用?


The example provided in this question is just for others to quickly reproduce the situation and may not use in the real world project.本题提供的例子仅供他人快速复现,实际项目中可能不会用到。 The answer is going to show how we can use some of the powered features of Swift答案将展示我们如何使用 Swift 的一些强大功能

I think that you cannot constrain an extension like that.认为你不能限制这样的扩展。 But you can define a constrained method instead:但是你可以定义一个受约束的方法:

extension Array {
    func customCompacted<T>() -> [T] where Element == GenericEnum<T> {
        return compactMap { $0.validValue }
    }
}

Usage examples:用法示例:

let genericInts = [GenericEnum.typed(1), GenericEnum.typed(2), GenericEnum.typed(3)]
print(genericInts.customCompacted()) // [1, 2, 3]

let genericDoubles = [GenericEnum.typed(1.0), GenericEnum.typed(2.0), GenericEnum.typed(3.0)]
print(genericDoubles.customCompacted()) // [1.0, 2.0, 3.0]

An alternative is to define a protocol to which GenericEnum conforms:另一种方法是定义一个GenericEnum符合的协议

enum GenericEnum<T> {
    case typed(T)
    case untyped(Error)
}

protocol GenericEnumProtocol {
    associatedtype T
    var validValue: T? { get }
}

extension GenericEnum: GenericEnumProtocol {
    var validValue: T? {
        if case .typed(let value) = self {
            return value
        } else {
            return nil
        }
    }
}

extension Array where Element : GenericEnumProtocol {
    var customCompacted: [Element.T] { // `Int` should be generic here
        compactMap { $0.validValue }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM