swift - 与泛型类型和协议关联的类型约束允许绕过 WHERE 子句
问题描述
为了完全理解问题,我在代码中评论了我的想法。为了便于查看,您可以将粘贴复制到 SWIFT 游乐场。不确定这是错误还是对类型推断的根本误解。我能够通过“欺骗”类型推断来绕过 WHERE 子句,让我发送不同的类型。
import Foundation
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
struct Stack<U>: Container {
var items = [U]()
mutating func push(_ item: U) {
items.append(item)
}
mutating func pop() -> U {
return items.removeLast()
}
// conformance to the Container protocol
mutating func append(_ item: U) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> U {
return items[i]
}
}
struct Devin {} /// This is to describe a custom type.
extension Devin: Container {
//typealias Item = String
/// NOTE: Eventhough the typealias is omitted, the type is inferred as String due to the pattern defined here.
mutating func append(_ item: String) {
/// do nothing
}
var count: Int {
/// do nothing
return 0
}
subscript(i: Int) -> String {
/// do nothing
return "0"
}
}
/// and here comes the confusing part.......
// Using a PROTOCOL in its ASSOCIATED TYPE'S constraints
protocol SuffixableContainer: Container {
/// Here we are saying SUFFIX like the ITEM constraint before is a type that conforms to this protocol being defined and the actual constraint is the where clause.
associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
/// Suffix has two constraints: It must conform to the SuffixableContainer protocol (the protocol currently being defined)
/// and its Item type must be the same as the container’s Item type. ( what does it look like when they arent the same? )
/// to understand whats happening here you have to see how its being used.
///
/// here is where its getting used and this is the important piece
func suffix(_ size: Int) -> Suffix
}
extension Stack: SuffixableContainer {
/// when conforming
func suffix(_ size: Int) -> Stack {
var result = Stack()
for index in (count-size)..<count {
result.append(self[index])
}
return result
}
// Inferred that Suffix is Stack.
}
/// in the commented out code below:
/// Eventhough, returning a stack in the generic form passed, returning one of a different type than the STRING that was inferred will throw an error.
/// Here we can return an instance of Devin and be fine but when we return an instance of a Stack that is a INT type it errors.
//extension Devin: SuffixableContainer {
// func suffix(_ size: Int) -> Stack<Int> {
// return Stack<Int>()
// }
//}
/// Error: note: requirement specified as 'Self.Item' == 'Self.Suffix.Item' [with Self = Devin]
/// This is the behavior you would expect
/// But here is a different story
/// Return ( Stack = just fine | Devin = just fine | Stack<Devin> = no go my friend )
extension Devin: SuffixableContainer {
func suffix(_ size: Int) -> Devin {
print(size)
return Devin()
}
}
print(Devin.Suffix.self)
print(Devin.Item.self)
if Devin.Item.self == Devin.Suffix.self {
print("It Worked")
} else {
print("They arent the same")
/// For those who can guess it will print that they aren't the same which violates the where clause
}
let store = Devin.suffix(Devin())
/// Not sure I understand why this works
/// now here is where I can really see the issue.
/// After printing the two types Devin does not equal String &
/// Devin and String are not the same types and the where clause didnt throw any error.
/// And for whatever reason I can pass the Devin object
解决方案
该where
子句不限制Devin.Suffix
为相同,因为Devin.Item
它限制Devin.Suffix.Item
为与其相同Devin.Item
。
保持其他所有内容不变,打印以下代码It Worked
。
print(Devin.Suffix.Item.self)
print(Devin.Item.self)
if Devin.Item.self == Devin.Suffix.Item.self {
print("It Worked")
} else {
print("They aren't the same")
}
推荐阅读
- powershell - PowerShell - 如何将以下脚本放在一起+调整页眉和页脚
- c++ - 是否可以在不使用 C++ 中的堆的情况下创建类 String?
- java - 找不到 XSOM 的权威源位置
- python - 我如何计算python字典中值的数量
- python - 如何使用漂亮的汤提取 HTML 中特定键的值
- c# - 如何管理具有多个连接的主键?
- security - 如何确定 Firefox 控制台就我的内容安全策略抱怨什么“内联脚本”?
- apache-kafka - 有没有办法使用 Kafka Connect 连接到多个主机中的多个数据库?
- matlab - 为多条曲线组合图例
- angular - Angular 元素之间的共享数据