使用 where 子句创建一个 Swift 扩展,该扩展对采用泛型的结构进行过滤

问题描述 投票:0回答:4

我正在尝试在

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 generics swift-extensions
4个回答
1
投票

这是 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

请注意您收到的错误消息。第一个错误,要求你添加 Any 实际上只是抱怨 Swift 无法谈论未参数化的泛型,并且当它不知道要建议什么类型时建议它是后备类型:Any。

但是

Set<Any>

不是“任何类型的集合”。这是一个集合,其中 Element == Any。所以 Any 必须是可哈希的,这是不可能的。并且

Set<Int>
不是
Set<Any>
的子类型。有完全不同的类型。所以这些错误有点令人困惑,并且会让你走上一条无益的道路。
    


0
投票
这是不可能的

。 where 子句需要特定的数据类型,并且简单地传递 Test 将不起作用,除非我指定更具体的内容,例如

Test<String>
感谢 Joakim 和 flanker 在评论中回答问题


0
投票

但是,如果通用参数具有共同属性,则可以使用

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

虽然我们无法直接访问泛型类型的属性,但我们可以访问任何其他常见属性。


-2
投票

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 协议的类型。

© www.soinside.com 2019 - 2024. All rights reserved.