Swift:相同类型的要求使通用参数等效?

Swift:相同类型的要求使通用参数等效?,swift,types,equivalent,Swift,Types,Equivalent,我正在使用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

我正在使用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
    }
}
协议基本协议{
关联T型
var str:T{get set}
}
结构物:碱性质子{
类型别名T=U
变量str:T
init(str:G),其中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 {
                                            ^
error: Test.playground:5:13: error: type 'Any' does not conform to protocol 'StringProtocol'
var array: [AItem<Any>] = [AItem(aStr: "asdfasdf")]
            ^

错误:测试。操场:10:45:错误:相同类型的要求使通用参数“G”和“U”等效
init(str:G),其中G==T{
^
如何使它们相等?或者我不能

谢谢


更新1:

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

我知道有“类型擦除”可能可以解决这个问题。所以我尝试了这种方法,但似乎不成功。上面提到的问题已经发生了


更新2:

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

var array: [AItem<Any>] = [AItem(aStr: "asdfasdf")]
struct AItem{
var aStr:T
}
变量数组:[AItem]=[AItem(aStr:“asdfasdf”)]
看,如果你编译这段代码,你会得到一个编译错误:

error: Test.playground:10:45: error: same-type requirement makes generic parameters 'G' and 'U' equivalent
    init<G: StringProtocol>(str: G) where G == T {
                                            ^
error: Test.playground:5:13: error: type 'Any' does not conform to protocol 'StringProtocol'
var array: [AItem<Any>] = [AItem(aStr: "asdfasdf")]
            ^

错误:测试。时间:5:13:错误:类型“Any”不符合协议“StringProtocol”
变量数组:[AItem]=[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
}
协议约束协议{}
扩展字符串:ConstraintProtocol{}
扩展数据:ConstraintProtocol{}
扩展名Int:ConstraintProtocol{}
.......
结构{
var aPara:T
初始(aPara:T){
self.aPara=aPara
}
}
//制作一个数组来包含它们
var anArray:[AItem]=[AItem(aPara:String)、AItem(aPara:1234)、AItem(aPara:Data(“路径”)、…]
//然后我可以使用数组中的任何项。也许我会实现一个方法来判断这些泛型并执行适当的操作。
混乱中的库里滕{
var结果=handleItem(curItem)
做点什么。。。
}
func handleItem(项目:AItem)->有吗{
如果(T项为…){
做点什么
返回。。。。。。
}如果(第T项为…){
做点什么
返回。。。
}
归零
}

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

似乎类型擦除是您问题的答案。类型擦除模式的关键思想是将强类型但不兼容的数据(如
AItem
AItem
)放在另一个数据结构中,该结构以“不太精确”的类型存储它们(通常是
任何

类型擦除的一个主要缺点是,如果以后需要恢复类型信息以确定需要对数组中的每个元素执行什么操作,则需要尝试将数据强制转换为每种可能的类型,这些类型可能比较混乱和脆弱。因此,我通常会尽可能避免使用类型信息

无论如何,这里有一个基于伪代码的类型擦除示例:

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"
}

如果泛型约束是
G==T
,那么你根本不需要这个方法是泛型的。只要使用
T
作为参数类型:
init(str:T)
,首先感谢你这么快的回复。是的,我知道在你提到的那种情况下,我不需要再添加一个“G”。如果我只使用“T”。我不能在数组中方便地使用此类。你是说
init(str:t)where U:StringProtocol
?你不能在数组中方便地使用此类是什么意思?询问你真正的问题(即,有数组的问题)。您问的是一个。抱歉,我的问题不清楚。这可以很好地解决func“init()”中出现的问题。但请参阅更新2…(我更新了该更新)这里仍然存在一些问题…我明白了。类型擦除似乎是一种方法,但我很好奇您打算如何使用此数组?
StringProtocol
具有关联的类型,这意味着它只能用作泛型约束,而不是“实型”约束类型。在不知道这些类型的情况下,使用
StringProtocol
实际上没有什么有用的,同样地,在不知道相关类型的情况下,使用包含
StringProtocol
的结构也没有什么用处.也许如果你展示你的用例,我可以更新我的答案来真正解决你的问题?好的,问题已经更新了我的答案。我花了半个小时来消化你的答案。你提供的第一种方法可能是我迄今为止知道的最可行的方法。我尝试了许多其他方法,但都失败了。这也是因为我不熟悉软件如果。你的回答为我提供了继续研究的方向。非常感谢!