Swift:Same-Type 要求使泛型参数等效?

问题描述

我正在使用 swift 5 并尝试编译以下代码:

protocol BasicProtocol {
    associatedtype T
    var str: T {get set}
}

struct AItem<U>: BasicProtocol {
    typealias T = U
    var str: T
    
    init<G: StringProtocol>(str: G) where G == T {
        self.str = str
    }
}

我遇到编译错误:

error: Test.playground:10:45: error: same-type requirement makes generic parameters 'G' and 'U' equivalent
    init<G: StringProtocol>(str: G) where G == T {
                                            ^

如何使它们等效?还是我不能?

谢谢。


更新 1:

这是我遇到的问题:我想声明struct“AItem”,希望它有一个泛型类型“T”。而且这个泛型类型会有一些限制,比如:“T:StringProtocol”。然后因为某种原因,我需要使用一个数组来加载这些结构体,并确保每个结构体的泛型可以随意设置。

我了解到有“类型擦除”可能可以解决这个问题。所以我尝试了这种方式,但似乎不成功。出现了上述问题。


更新 2:

struct AItem<T: StringProtocol> {
    var aStr: T
}

var array: [AItem<Any>] = [AItem(aStr: "asdfasdf")]

看,如果你编译这段代码,你会得到一个编译错误:

error: Test.playground:5:13: error: type 'Any' does not conform to protocol 'StringProtocol'
var array: [AItem<Any>] = [AItem(aStr: "asdfasdf")]
            ^

如果我使用“var array: [AItem]”,我将无法在数组中放置任何其他非“String”但实现“StringProtocol”的实例。

这就是为什么我说我要“确保每个结构的泛型可以随意设置”。


更新 3:

非常感谢@jweightman,现在我再次更新我的问题。

protocol ConstraintProtocol {}

extension String: ConstraintProtocol{}
extension Data: ConstraintProtocol{}
extension Int: ConstraintProtocol{}
.......

struct AItem<T = which class has Implemented "ConstraintProtocol"> {
    var aPara: T
    init(aPara:T) {
        self.aPara = aPara
    }
}
// make a array to contain them
var anArray: [AItem<Any class which Implemented "ConstraintProtocol">] = [AItem(aPara: "String"),AItem(aPara: 1234),AItem(aPara: Data("a path")),…]

// then I can use any item which in anArray. Maybe I will implement a method to judge these generics and perform the appropriate action.
for curItem in anArray {
    var result = handleItem(curItem)
    do something...
}


func handleItem<T: ConstraintProtocol>(item: AItem<T>) -> Any? {

    if (item.T is ...) {
        do someThing
        return ......
    } else if (item.T is ...) {
        do someThing
        return ...
    }
    return nil
}

这是我的全部想法,但都是伪代码。

解决方法

似乎类型擦除是您问题的答案。类型擦除模式的关键思想是将强类型但不兼容的数据(如 AItem<String>AItem<Data>)放在另一个数据结构中,该数据结构以“不太精确”的类型(通常是 { {1}})。

类型擦除的一个主要缺点是你丢弃了类型信息——如果你稍后需要恢复它来弄清楚你需要对数组中的每个元素做什么,你需要尝试强制转换你的数据到每种可能的类型,这可能是混乱和脆弱的。出于这个原因,我通常会尽量避免它。

无论如何,这是基于您的伪代码的类型擦除示例:

Any

您可以考虑的另一种类型擦除的替代方法,如果您的泛型可以采用一个小的、有限的具体类型集,它会很好地工作,将使用具有关联值的枚举来定义“总和类型”。如果您感兴趣的协议来自您无法更改的库,这可能不是一个好的选择。实际上,总和类型可能如下所示:

protocol ConstraintProtocol {}

extension String: ConstraintProtocol{}
extension Data: ConstraintProtocol{}
extension Int: ConstraintProtocol{}

struct AItem<T: ConstraintProtocol> {
    var aPara: T

    init(aPara: T) {
        self.aPara = aPara
    }
}

struct AnyAItem {
    // By construction,this is always some kind of AItem. The loss of type
    // safety here is one of the costs of the type erasure pattern.
    let wrapped: Any
    
    // Note: all the constructors always initialize `wrapped` to an `AItem`.
    // Since the member variable is constant,our program is "type correct"
    // even though type erasure isn't "type safe."
    init<T: ConstraintProtocol>(_ wrapped: AItem<T>) {
        self.wrapped = wrapped
    }
    
    init<T: ConstraintProtocol>(aPara: T) {
        self.wrapped = AItem(aPara: aPara);
    }
    
    // Think about why AnyAItem cannot expose any properties of `wrapped`...
}

var anArray: [AnyAItem] = [
    AnyAItem(aPara: "String"),AnyAItem(aPara: 1234),AnyAItem(aPara: "a path".data(using: .utf8)!)
]

for curItem in anArray {
    let result = handleItem(item: curItem)
    print("result = \(result)")
}

// Note that this function is no longer generic. If you want to try to "recover"
// the type information you erased,you will have to do that somewhere. It's up
// to you where you want to do this.
func handleItem(item: AnyAItem) -> String {
    if (item.wrapped is AItem<String>) {
        return "String"
    } else if (item.wrapped is AItem<Data>) {
        return "Data"
    } else if (item.wrapped is AItem<Int>) {
        return "Int"
    }
    return "unknown"
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...