Swift does not allow recursive structs, but there is a very simple trick to get them anyway.
Note: I know that you can directly use only an enum to get the same result. I just want to point out that in this way it is possible to obtain recursive structs.
A simple immutable linked list struct based:
// the trick
enum Recurse<T> {
indirect case next(T)
var data : T {
switch self {
case .next( let data ): return data
}
}
}
// a basic immutable single linked list struct based
struct LinkedList<Element> {
let head: Element?
private let next: Recurse<LinkedList<Element>>?
var tail : LinkedList<Element>? {
return next?.data ?? nil
}
init() {
self.head = nil
self.next = nil
}
init( head:Element, tail: LinkedList<Element>? = nil ) {
self.head = head
self.next = tail.flatMap { Recurse.next( $0 ) }
}
}
// a characterList example:
extension LinkedList where Element == Character {
init<T:StringProtocol>( string:T ) {
if let character = string.first {
self.init( head:character, tail:LinkedList( string: string.dropFirst() ) )
} else {
self.init()
}
}
}
extension LinkedList : CustomStringConvertible where Element == Character {
var description: String {
return head.flatMap { String( $0 ) + (tail?.description ?? "") } ?? ""
}
}
let characterList = LinkedList( string: "Hello World!" )
print( characterList )
@propertyWrapper
enum Indirect<T> {
indirect case next(T)
init(wrappedValue: T) {
self = .next(wrappedValue)
}
var wrappedValue: T {
switch self {
case .next(let value): return value
}
}
}
struct LinkedList<T> {
let head: T
@Indirect var tail: LinkedList<T>?
}
The struct will get a synthesized initializer that does the rihght thing. If you want to write your own initializer, you must either initialize the underlying _tail property like so:
@propertyWrapper
enum Indirect<T> {
indirect case next(T)
init(wrappedValue: T) {
self = .next(wrappedValue)
}
var wrappedValue: T {
switch self {
case .next(let value): return value
}
}
}
struct LinkedList<Element> {
let head: Element?
@Indirect var tail: LinkedList<Element>?
init() {
self.head = nil
self._tail = .next(nil)
}
init(head: Element, tail: LinkedList<Element>? = nil) {
self.head = head
self._tail = .next(tail)
}
}
… or make the @Indirect wrapper mutable by creating a setter, like suggested above. However, I made it immutable to mimic the let for the head.
It's a bit unfortunate that the indirect optional will make a useless heap allocation when storing a nil value. This is probably slightly less wasteful:
@propertyWrapper
enum IndirectOptional<T> {
case none
indirect case some(T)
init(wrappedValue: T?) {
if let wrappedValue = wrappedValue {
self = .some(wrappedValue)
} else {
self = .none
}
}
var wrappedValue: T? {
get {
switch self {
case .none: return nil
case .some(let value): return value
}
}
set {
if let newValue = newValue {
self = .some(newValue)
} else {
self = .none
}
}
}
}
struct LinkedList<T> {
let head: T
@IndirectOptional var tail: LinkedList<T>?
}
sorry to bring up an old thread, but i also came across this issue and tried this example encountering the same crash even in swift 5.8
i was able to determine that it looks like the stack was busted (call stack too deep) and it seems setting the stack size to 16MB* allows larger recursion up to at least 8000 (compared to 6000 in the example)
this looks like a compiler bug, or perhaps its by design.. in which case i wonder if there is a specific swift-centric annotation that can turn that dealloc into a loop instead of expanding like that...