我正在尝试在
Set
上创建一个扩展,它使用 where 子句,以便它仅适用于我接受泛型的结构。但我一直遇到关于扩展希望在结构中定义泛型的错误
在此示例中,我收到以下错误,编译器提示建议我使用
<Any>
:对泛型类型“Test”的引用需要<...>中的参数
struct Test<T> {
var value : T
func printIt() {
print("value")
}
}
extension Set where Element == Test {
}
但是,当我在结构中使用
<Any>
时,我收到此错误:相同类型约束类型“Test”不符合所需协议“Equatable”
extension Set where Element == Test<Any> {
}
关于如何让 where 子句接受我在泛型中使用的任何类型的 Test 结构,有什么建议吗?
感谢您的帮助
这是 Swift 类型系统的限制。如果没有具体的类型参数,就无法讨论泛型类型,即使这些类型参数与类型的使用无关。对于这种特殊情况(所有可能的类型参数的扩展),我不认为存在任何深层问题可以阻止这种情况。它是“参数化扩展”的简单版本,这是一个理想的功能。只是不支持(尽管正在实施)。 当今解决此问题的标准方法是使用协议。
首先,进行一些与您的问题无关的清理(您可能已经知道)。您的示例要求 Test 为
Hashable
:
struct Test<T: Hashable>: Hashable {
var value : T
func printIt() {
print("value")
}
}
制定一个协议,要求扩展所需的任何部分,并使测试符合:
protocol PrintItable {
func printIt()
}
extension Test: PrintItable {}
然后使用协议而不是类型:
extension Set where Element: PrintItable {
func printAll() {
for item in self { item.printIt() }
}
}
let s: Set<Test<Int>> = [Test(value: 1)]
s.printAll() // value
但是
Set<Any>
不是“任何类型的集合”。这是一个集合,其中 Element == Any。所以 Any 必须是可哈希的,这是不可能的。并且
Set<Int>
不是 Set<Any>
的子类型。有完全不同的类型。所以这些错误有点令人困惑,并且会让你走上一条无益的道路。。 where 子句需要特定的数据类型,并且简单地传递 Test
将不起作用,除非我指定更具体的内容,例如
Test<String>
。感谢 Joakim 和 flanker 在评论中回答问题
但是,如果通用参数具有共同属性,则可以使用
protocol
尝试不同的方法。例如:
protocol IsMyObject: Hashable {
var nickname: String { get set }
func printType()
}
struct MyObject<T>: IsMyObject {
var value: T
var nickname: String
func printType() {
print(type(of: value))
}
// Should conform Hashable...
}
extension Set where Element: IsMyObject {
func longestNickname() {
reduce("") { max($0, $1.nickname) }
}
func printAllTypes() {
forEach { printType() }
}
}
var mySet: Set<MyObject<Int>> = [MyObject(value: 2024, nickname: "This year"), MyObject(value: 1024, nickname: "Hello, World")]
let longestNickname = mySet.longestNickname // "Hello, World"
您还可以存储不同类型的元素:
// You should erase the type to avoid ambiguous generic parameters
class AnyMyObject: IsMyObject {
private var store: any IsMyObject
var nickname: String {
get { store.nickname }
set { store.nickname = newValue }
}
init(_ object: any IsMyObject) {
self.store = object
}
func printType() {
store.printType()
}
// Hashable conformance
func hash(into hasher: inout Hasher) {
store.hash(into: &hasher)
}
static func == (lhs: IsMyObject, rhs: IsMyObject) -> Bool {
lhs.hashValue == rhs.hashValue
}
}
let integer = MyObject<Int>(value: 9999, nickname: "NeinNeinNeinNein!!")
let string = MyObject<String>(value: "Hello world", nickname: "String type")
let variadicSet: Set<AnyMyObject> = [AnyMyObject(integer), AnyMyObject(string)]
variadicSet.printAllTypes() // Prints "String", "Int" in random order
虽然我们无法直接访问泛型类型的属性,但我们可以访问任何其他常见属性。
Hashable 协议。
您的结构必须如下所示。
struct Test<T: Hashable> : Hashable {
var value : T
func printIt() {
print("value")
}
func hash(into hasher: inout Hasher) {
hasher.combine(value.hashValue)
}
}
因此您不能使用
Any 作为您的扩展,您必须指定符合 Hashable 协议的类型。