swift 符合 Stridable 后进入无限循环
swift breaks into infinite loop after conforming to Stridable
我有一个这样的枚举:
enum Rank: CaseIterable {
case Ace, King, Queen, ...
}
并希望使其符合Stridable
。我试图通过像这样获取卡片索引来实现 func distance(to other: _)
:
func distance(to other: Rank) -> Int {
let indexOfSelf = Rank.allCases.firstIndex(of: self)!
let indexOfOther = Rank.allCases.firstIndex(of: other)!
return indexOfOther - indexOfSelf
}
只要我不符合 Stridable(例如, ace.distanceTo(Queen)
结果为 2。但是,一旦我符合 Stridable
,代码就会进入无限循环,我得到错误 EXC_BAD_ACCESS (code=2, address=x)
。
这是应该发生的吗?如果是这样,为什么会这样?
感谢大家的帮助!
我的实现advanced(by n: Int)
:
func advanced(by n: Int) -> Rank {
let index = Rank.allCases.firstIndex(of: self)!
let resultIndex = index + n
if resultIndex > Rank.allCases.count {
return .Two
}
return Rank.allCases[resultIndex]
}
会导致错误的东西:
调用:ace.distanceTo(Queen)
会进入无限循环
来自 Strideable 协议的文档:
Important
The Strideable protocol provides default implementations for the equal-to (==) and less-than (<) operators that depend on the Stride type’s implementations. If a type conforming to Strideable is its own Stride type, it must provide concrete implementations of the two operators to avoid infinite recursion.
在你的例子中,Rank
不是它自己的 Stride
类型,而是 distance(to:)
方法调用 firstIndex(of:)
,它在元素上调用 ==
。现在 ==
有一个针对 Strideable
类型的特殊实现——正如我们从 Stride.swift 中的实现中看到的那样——调用 distance(to:)
。这会导致“无限”递归,并最终导致堆栈溢出。您还可以从堆栈回溯中看到:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ffeef3fffb8)
* frame #0: 0x0000000100001b4b test`Rank.distance(other=ace, self=king) at main.swift:11
frame #1: 0x0000000100001fd8 test`protocol witness for Strideable.distance(to:) in conformance Rank at <compiler-generated>:0
frame #2: 0x00007fff7ac4927b libswiftCore.dylib`static (extension in Swift):Swift.Strideable.== infix(A, A) -> Swift.Bool + 219
frame #3: 0x0000000100001f56 test`protocol witness for static Equatable.== infix(_:_:) in conformance Rank at <compiler-generated>:0
frame #4: 0x00007fff7ab25ea9 libswiftCore.dylib`(extension in Swift):Swift.Collection< where A.Element: Swift.Equatable>.firstIndex(of: A.Element) -> Swift.Optional<A.Index> + 905
frame #5: 0x0000000100001c31 test`Rank.distance(other=ace, self=ace) at main.swift:12:49
...
frame #42256: 0x0000000100001fd8 test`protocol witness for Strideable.distance(to:) in conformance Rank at <compiler-generated>:0
frame #42257: 0x00007fff7ac4927b libswiftCore.dylib`static (extension in Swift):Swift.Strideable.== infix(A, A) -> Swift.Bool + 219
frame #42258: 0x0000000100001f56 test`protocol witness for static Equatable.== infix(_:_:) in conformance Rank at <compiler-generated>:0
frame #42259: 0x00007fff7ab25ea9 libswiftCore.dylib`(extension in Swift):Swift.Collection< where A.Element: Swift.Equatable>.firstIndex(of: A.Element) -> Swift.Optional<A.Index> + 905
frame #42260: 0x0000000100001c31 test`Rank.distance(other=ace, self=ace) at main.swift:12:49
frame #42261: 0x0000000100001fd8 test`protocol witness for Strideable.distance(to:) in conformance Rank at <compiler-generated>:0
frame #42262: 0x00007fff7ac4927b libswiftCore.dylib`static (extension in Swift):Swift.Strideable.== infix(A, A) -> Swift.Bool + 219
frame #42263: 0x0000000100001f56 test`protocol witness for static Equatable.== infix(_:_:) in conformance Rank at <compiler-generated>:0
frame #42264: 0x00007fff7ab25ea9 libswiftCore.dylib`(extension in Swift):Swift.Collection< where A.Element: Swift.Equatable>.firstIndex(of: A.Element) -> Swift.Optional<A.Index> + 905
frame #42265: 0x0000000100001c31 test`Rank.distance(other=queen, self=ace) at main.swift:12:49
frame #42266: 0x0000000100001830 test`main at main.swift:23:18
frame #42267: 0x00007fff7b3843d5 libdyld.dylib`start + 1
(lldb)
正如 Joakim 所说,最简单的解决方案是根据枚举的原始值实现这些方法,这避免了(递归)使用 ==
:
enum Rank: Int, Strideable {
case ace, king, queen, jack
func advanced(by n: Int) -> Rank {
return Self(rawValue: self.rawValue + n)!
}
func distance(to other: Rank) -> Int {
return other.rawValue - self.rawValue
}
}
这也比一遍又一遍地查找 allCases
集合中的值更有效。
我有一个这样的枚举:
enum Rank: CaseIterable {
case Ace, King, Queen, ...
}
并希望使其符合Stridable
。我试图通过像这样获取卡片索引来实现 func distance(to other: _)
:
func distance(to other: Rank) -> Int {
let indexOfSelf = Rank.allCases.firstIndex(of: self)!
let indexOfOther = Rank.allCases.firstIndex(of: other)!
return indexOfOther - indexOfSelf
}
只要我不符合 Stridable(例如, ace.distanceTo(Queen)
结果为 2。但是,一旦我符合 Stridable
,代码就会进入无限循环,我得到错误 EXC_BAD_ACCESS (code=2, address=x)
。
这是应该发生的吗?如果是这样,为什么会这样?
感谢大家的帮助!
我的实现advanced(by n: Int)
:
func advanced(by n: Int) -> Rank {
let index = Rank.allCases.firstIndex(of: self)!
let resultIndex = index + n
if resultIndex > Rank.allCases.count {
return .Two
}
return Rank.allCases[resultIndex]
}
会导致错误的东西:
调用:ace.distanceTo(Queen)
会进入无限循环
来自 Strideable 协议的文档:
Important
The Strideable protocol provides default implementations for the equal-to (==) and less-than (<) operators that depend on the Stride type’s implementations. If a type conforming to Strideable is its own Stride type, it must provide concrete implementations of the two operators to avoid infinite recursion.
在你的例子中,Rank
不是它自己的 Stride
类型,而是 distance(to:)
方法调用 firstIndex(of:)
,它在元素上调用 ==
。现在 ==
有一个针对 Strideable
类型的特殊实现——正如我们从 Stride.swift 中的实现中看到的那样——调用 distance(to:)
。这会导致“无限”递归,并最终导致堆栈溢出。您还可以从堆栈回溯中看到:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ffeef3fffb8)
* frame #0: 0x0000000100001b4b test`Rank.distance(other=ace, self=king) at main.swift:11
frame #1: 0x0000000100001fd8 test`protocol witness for Strideable.distance(to:) in conformance Rank at <compiler-generated>:0
frame #2: 0x00007fff7ac4927b libswiftCore.dylib`static (extension in Swift):Swift.Strideable.== infix(A, A) -> Swift.Bool + 219
frame #3: 0x0000000100001f56 test`protocol witness for static Equatable.== infix(_:_:) in conformance Rank at <compiler-generated>:0
frame #4: 0x00007fff7ab25ea9 libswiftCore.dylib`(extension in Swift):Swift.Collection< where A.Element: Swift.Equatable>.firstIndex(of: A.Element) -> Swift.Optional<A.Index> + 905
frame #5: 0x0000000100001c31 test`Rank.distance(other=ace, self=ace) at main.swift:12:49
...
frame #42256: 0x0000000100001fd8 test`protocol witness for Strideable.distance(to:) in conformance Rank at <compiler-generated>:0
frame #42257: 0x00007fff7ac4927b libswiftCore.dylib`static (extension in Swift):Swift.Strideable.== infix(A, A) -> Swift.Bool + 219
frame #42258: 0x0000000100001f56 test`protocol witness for static Equatable.== infix(_:_:) in conformance Rank at <compiler-generated>:0
frame #42259: 0x00007fff7ab25ea9 libswiftCore.dylib`(extension in Swift):Swift.Collection< where A.Element: Swift.Equatable>.firstIndex(of: A.Element) -> Swift.Optional<A.Index> + 905
frame #42260: 0x0000000100001c31 test`Rank.distance(other=ace, self=ace) at main.swift:12:49
frame #42261: 0x0000000100001fd8 test`protocol witness for Strideable.distance(to:) in conformance Rank at <compiler-generated>:0
frame #42262: 0x00007fff7ac4927b libswiftCore.dylib`static (extension in Swift):Swift.Strideable.== infix(A, A) -> Swift.Bool + 219
frame #42263: 0x0000000100001f56 test`protocol witness for static Equatable.== infix(_:_:) in conformance Rank at <compiler-generated>:0
frame #42264: 0x00007fff7ab25ea9 libswiftCore.dylib`(extension in Swift):Swift.Collection< where A.Element: Swift.Equatable>.firstIndex(of: A.Element) -> Swift.Optional<A.Index> + 905
frame #42265: 0x0000000100001c31 test`Rank.distance(other=queen, self=ace) at main.swift:12:49
frame #42266: 0x0000000100001830 test`main at main.swift:23:18
frame #42267: 0x00007fff7b3843d5 libdyld.dylib`start + 1
(lldb)
正如 Joakim 所说,最简单的解决方案是根据枚举的原始值实现这些方法,这避免了(递归)使用 ==
:
enum Rank: Int, Strideable {
case ace, king, queen, jack
func advanced(by n: Int) -> Rank {
return Self(rawValue: self.rawValue + n)!
}
func distance(to other: Rank) -> Int {
return other.rawValue - self.rawValue
}
}
这也比一遍又一遍地查找 allCases
集合中的值更有效。