[英]How to constrain a Swift generic type parameter to be any protocol and another parameter to conform to it?
[英]How to constrain a generic sequence parameter to a tuple in Swift 3?
在Swift 2中,我能够编写一个可对任何序列进行操作的函数,例如(String, Int)
。 它看起来像这样:
func test<T: SequenceType where T.Generator.Element == (String, Int)>(input: T) {
for (k, v) in input {
print(k, "=", String(v))
}
}
使用元组作为约束类型特别有用,因此它可以接受字典,例如[String:Int]
,因为它们的序列类型由元组组成。
在Swift 3中,我相信类似的功能将是:
func test<T: Sequence>(input: T) where T.Iterator.Element == (String, Int) {
for (k, v) in input {
print(k, "=", String(v))
}
}
但是尝试传递[String:Int]
,例如: test(input: ["a": 1, "b": 2])
,将导致错误:
无法推断通用参数“ T”
据我所知,Swift 3中的字典仍然使用(Key,Value)元组作为其迭代器类型,因此我认为这应该可行。 实际上,如果我不使用单一类型作为受约束的迭代器类型,例如where T.Iterator.Element == String
,那么我可以传入诸如[String]
东西,并且它可以正常工作。
我缺少什么,或者这可能是Swift 3中的回归?
一个有趣的例子。
让我们检查关于符合Sequence
的Dictionary
的定义:
public func makeIterator() -> DictionaryIterator<Key, Value>
然后DictionaryIterator
:
public mutating func next() -> (key: Key, value: Value)?
因此,对于Dictionary
, T.Iterator.Element
似乎是(key: Key, value: Value)
,而不是(Key, Value)
。
如果将函数重写为:
func test<T: Sequence>(input: T) where T.Iterator.Element == (key: String, value: Int) {
for (k, v) in input {
print(k, "=", String(v))
}
}
这有效:
test(input: ["a": 1, "b": 2])
但这不起作用:
test(input: [("a", 1),("b",2)]) //->Generic parameter 'T' could not be inferred
我不确定这是预期的功能还是某种回归,还是仅仅是一个错误。
这是已知的错误( SR-992 ),在某些情况下,该错误会阻止编译器匹配相同类型但标签不同的元组。
一种可能允许您同时传递(String, Int)
或(key: String, value: Int)
元组和[String : Int]
字典的序列的可能解决方法是使test(input:)
重载期望的函数以(key: String, value: Int)
元素作为输入的序列。 然后,您可以使用懒惰求值的map
来“擦除”元组标签,然后将该序列传递给test
的原始实现。
// Overload to deal with [String : Int] inputs – workaround for a bug.
func test<T: Sequence>(input: T) where T.Iterator.Element == (key: String, value: Int) {
// 'Erase' the tuple labels with a lazily evaluated map(_:)
// and pass the sequence onto the original implementation of test(input:)
test(input: AnySequence(input.lazy.map{($0.key, $0.value)}))
}
func test<T: Sequence>(input: T) where T.Iterator.Element == (String, Int) {
for (k, v) in input {
print(k, "=", v)
}
}
let d = ["foo" : 5]
let a = [("key", 3)]
test(input: d) // prints: foo = 5
test(input: a) // prints: key = 3
也许不是最理想的解决方案-但据我所知,没有其他简单的解决方法可以解决此问题。 如果有人有更好的想法,肯定会感兴趣。
这不是快速的3。但是,它可能会有所帮助。
尚不允许参数化扩展。 因此,我们需要使用功能。
extension Result {
func zip<A, B, C>(with other: Result<C, Failure>) -> Result<(A, B, C), Failure> where Success == (A, B) {
return flatMap { (a, b) in
Result<(A, B, C), Failure>.init(catching: {
let c = try other.get()
return (a, b, c)
})
}
}
}
Result
在哪里
public enum Result<Success, Failure: Error> {
/// A success, storing a `Success` value.
case success(Success)
/// A failure, storing a `Failure` value.
case failure(Failure)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.