在Swift中用一个泛型约束另一个泛型

在Swift中用一个泛型约束另一个泛型,swift,generics,protocols,generic-constraints,Swift,Generics,Protocols,Generic Constraints,我遇到了一个问题,我有一些协议: protocol Baz { func bar<T>(input:T) } 这会产生错误,因为编译器不知道S和t是同一类型。理想情况下,我应该能够写出以下内容: func bar<T where T==S>(input:T) { value = input } 功能条(输入:T){ 值=输入 } 或 功能条(输入:T){ 值=输入 } 第一种形式给出了一个“相同类型的需求使泛型参数'S'和'

我遇到了一个问题,我有一些协议:

protocol Baz {
    func bar<T>(input:T)
}
这会产生错误,因为编译器不知道
S
t
是同一类型。理想情况下,我应该能够写出以下内容:

    func bar<T where T==S>(input:T) {
        value = input
    }
功能条(输入:T){
值=输入
}

功能条(输入:T){
值=输入
}
第一种形式给出了一个“相同类型的需求使泛型参数'S'和'T'等效”错误(这正是我试图做的,所以不确定为什么它是一个错误)。第二种形式是“从非协议、非类类型的继承”

您对如何实现这一点有何想法,或者对Swift中更好的设计模式有何想法

更新:正如@luk2302所指出的,我忘了让
Foo
遵守
Baz
协议

编辑的(“…一个错误,因为编译器不知道S和t是同一类型。”)

首先:这只是一个单独的注释(也许是为了弥补我之前的答案,最终我自己追赶自己的尾巴去计算大量的冗余代码),除了Robs的优秀答案

以下解决方法可能会让您的实现
协议Foo…
/
类Bas:Foo…
模拟您最初要求的行为,从这个意义上讲,类方法
条(…)
将知道泛型
S
T
是否实际上是同一类型,当
S
不是
T
的同一类型时,Foo仍然符合协议

protocol Baz {
    func bar<T>(input:T)
}

class Foo<S>: Baz {
    var value:S

    init(value:S) {
        self.value = value
    }

    func bar<T>(input:T) {
        if input is S {
            value = input as! S
        }
        else {
            print("Incompatible types. Throw...")
        }
    }
}

// ok
var a = Foo(value: 1.0) // Foo<Double>
print(a.value) // 1.0
a.bar(2.0)
print(a.value) // 2.0
let myInt = 1
a.bar(myInt) // Incompatible types. Throw...
print(a.value) // 2.0

// perhaps not a loophole we indended
let myAny : Any = 3.0
a.bar(myAny)
print(a.value) // 3.0

然而,这将
Any
AnyObject
完全排除在泛型之外(不仅针对“漏洞铸造”),这可能不是一种受欢迎的行为。

@luk2302在评论中暗示了这一点,但只是为了让未来的搜索者明白这一点

protocol Baz {
    func bar<T>(input:T)
}
你很可能是指(并暗示你的意思):

正如您所注意到的,这使协议成为PAT(具有关联类型的协议),这意味着您不能将其直接放入集合中。正如您所注意到的,如果您真的需要它们的集合,通常的解决方案是。如果斯威夫特能自动为你写下橡皮擦,那就太好了,它很可能在将来能做到这一点,并能消除这个问题。这就是说,尽管有点乏味,但书写型橡皮擦非常简单

现在,虽然不能将PAT直接放入集合,但可以将一般约束的PAT放入集合。因此,只要将集合包装为约束T的类型,就仍然没有问题

如果这些变得复杂,约束代码可能会变得单调乏味,并且非常重复。然而,这可以通过一些技术来改进

可以使用带有静态方法的泛型结构来避免对自由函数重复提供约束

协议可以转换为通用结构(这将类型橡皮擦形式化为主要类型,而不是“根据需要”)

在许多情况下,可以用函数替换协议。例如,鉴于此:

protocol Bar {
    typealias T
    func bar(input: T)
}

struct Foo : Bar {
    func bar(input: Int) {}
}
你不能这样做:

 let bars: [Bar] = [Foo()] // error: protocol 'Bar' can only be used as a generic constraint because it has Self or associated type requirements
但是你可以很容易地做到这一点,这同样好:

let bars = [(Int) -> Void] = [Foo().bar]
这对于单一方法协议尤其强大

协议、泛型和函数的混合比试图将所有内容强制放入协议盒要强大得多,至少在协议添加一些缺失的功能以实现其承诺之前是如此


(对一个特定的问题给出具体的建议会更容易。没有一个答案可以解决所有的问题。)

我担心这是不可能的:假设您在
Baz
上添加了一个类型约束,比如T必须符合Hashable。当您希望Foo符合Baz时,您不能使用它的通用参数
S
实现bar,因为不能保证
S
也符合
哈希值。。。两件事:(1)我认为
bar
的通用参数
S
与协议本身无关;(2) 我同意,如果没有某种类型的类型约束,编译器就无法知道
S
T
是相同的类型。但我不明白的是,为什么我不能用约束来强制执行它。你不能用约束来强制执行它,因为如果你说
t,其中t==S
,而t根本不是S,那么这个方法会怎么样?您的类将不再符合协议:/啊,我明白了。我想我已经习惯C++了。这是不幸的,因为这意味着没有好的方法可以避免协议本身是泛型的,这样做会迫使我也编写类型擦除类…@RobNapier好的,这就是我的意思。在过去的几天里,我遇到了很多关于泛型和协议的问题,在这个问题上调用泛型可能有点苛刻——它们并没有提供人们可能期望的所有功能。例如,为给定的类型约束使用协议扩展一个类。问题是,我正在努力防止协议具有
Self
,因此在原始示例中没有
typealias U
。如果你这样做,你就解决了眼前的问题,但却产生了一个新的问题,你不能只保留那种类型的集合(因为有一个
自我
)。对于我的实际代码(不是上面给出的示例),我认为解决这个问题的唯一方法是使用类型擦除,它可以添加大量额外的代码。这是不必要的,而且我认为会给您带来麻烦:
func bar(input:T)
。我相信你在这里的意思是功能条(输入:U)
。如果必须是
U
,为什么要声明
T
?例如,这提供了您在不使用
\U MyTypeSeparator
的情况下描述的相同行为@感谢您的反馈!
protocol Baz {
    func bar(input:Any)
}
protocol Baz {
    typealias T
    func bar(input: T)
}
protocol Bar {
    typealias T
    func bar(input: T)
}

struct Foo : Bar {
    func bar(input: Int) {}
}
 let bars: [Bar] = [Foo()] // error: protocol 'Bar' can only be used as a generic constraint because it has Self or associated type requirements
let bars = [(Int) -> Void] = [Foo().bar]