Swift struct type recursion
The answer is in your question: structs are value types. If you include a substruct B
into a struct A
, it means, that one object of type A
will have a size sizeof(all_other_fields_of_A) + sizeof(B)
. So, a value type can not be recursive: it would have infinite size.
Recursive generic structs with different types in Swift 4
You have 2 options for doing this.
The simple solution, improving on @Cristik's answer, is to have another initialiser that doesn't expect another generic parameter:
struct Lock<Element> {
var element: Element
init(_ element: Element) {
self.element = element
}
init<T>(_ element: Element, _ args: [Lock<T>]?) {
self.init(element)
}
}
You can then have the code you want above, but you lose the information that Element
conforms to Hashable
.
Your second option is to create a protocol
using an associatedtype
. Using a similar trick with having 2 init
s, you can do the same except explicitly defining the types:
protocol Lock {
associatedtype Element
init(_ element: Element)
init<T>(_ element: Element, _ args: [T]?) where T: Lock
}
struct HashableLock<H: Hashable>: Lock {
typealias Element = H
var element: Element
init(_ element: Element) {
self.element = element
}
init<T>(_ element: Element, _ args: [T]?) where T: Lock {
self.init(element)
}
}
struct IntLock: Lock {
typealias Element = Int
var element: Int
init(_ element: Int) {
self.element = element
}
init<T>(_ element: Int, _ args: [T]?) where T: Lock {
self.init(element)
}
}
Then you can create locks like this:
let stringStringLock = HashableLock("element", [HashableLock("100")])
let stringIntLock = HashableLock("element", [IntLock(100)])
The first version is a lot cleaner but it's more limiting. It's up to you which one to use, depends on your needs.
Swift: Recursive Value Type
TL;DR:
What you are trying to achieve is easily accomplished using split:
for s in split("first/second/third", { c in c == "/" } ) {
println("\(s)")
}
Discussion:
It seems like you are trying to write a linked list of value types. The problem is that Swift couples the concepts of copy semantics with value / reference access. (unlike say C++ which allows you to create the same object on the stack or heap). The solution would seem to be wrapping it in a reference container aka class.
class SplitString { //splits a string into parts before and after the first "/"
var preSlash: String = String()
var postSlash: Wrapped? = nil
init(_ str: String) {
var arr = Array(str)
var x = 0
for ; x < arr.count && arr[x] != "/"; x++ { preSlash.append(arr[x]) }
if x + 1 < arr.count { //if there is a slash
var postSlashStr = String()
for x++; x < arr.count; x++ {
postSlashStr.append(arr[x])
}
postSlash = Wrapped(postSlashStr)
}
}
}
class Wrapped {
var split:SplitString
init(var _ str:String) {
split = SplitString(str)
}
}
Note that this code compiles as a proof of concept but I haven't delved into your algorithm or tested it.
Edit:
In response to your edits above, this code will exercise your code above and yield the splits you want:
for (var s:SplitString? = split; s != nil; s = s?.postSlash?.split) {
println("\(s!.preSlash)")
}
Obviously having turtles all the way down doesn't make sense, per the discussion above, so you need to break the cycle, as was the case with the class containing your struct.
Note that I have been trying to answer the question that you posted, not the problem that you have. The solution to the problem that you have, is to use SequenceOf and GeneratorOf to create a sequence wrapped generator that iterates through the slashes and returns the substrings between. This is actually done for you via the split function:
for s in split("first/second/third", { c in c == "/" } ) {
println("\(s)")
}
Swift Codable struct recursively containing itself as property
A simple way is to just change the struct into a class:
class Message: Codable {
let content: String
// ...other values
let reference: Message? // optional - the recursion has to end somewhere right?
}
But this could break other parts of your code, since structs and classes have vastly different semantics.
An alternative would be to make a reference type Box
:
class Box<T: Codable>: Codable {
let wrappedValue: T
required init(from decoder: Decoder) throws {
wrappedValue = try T(from: decoder)
}
func encode(to encoder: Encoder) throws {
try wrappedValue.encode(to: encoder)
}
}
Then,
struct Message: Codable {
let content: String
// ...other values
let boxedReference: Box<Message>?
// you can still refer to 'reference' as 'reference' in your swift code
var reference: Message? { boxedReference?.wrappedValue }
enum CodingKeys: String, CodingKey {
case content, boxedReference = "reference"
}
}
Asynchronous closure recursion for structs
Consider this code block
let reply = try self.jsonDecoder.decode(Comment.self, from: data)
replies[replyIndex] = reply
if reply.repliesIDs != nil {
self.fetchReplies(for: reply) { replies in
reply.replies = replies
}
}
If Comment
was a struct, this will fetching reply
, adding a copy of it to the replies
array, and then, in fetchReplies
you are mutating the original reply
(which you must have changed from let
to var
for this line to even compile), not the copy in the array.
So, you might want to refer to replies[replyIndex]
in your fetchReplies
closure, e.g.:
let reply = try self.jsonDecoder.decode(Comment.self, from: data)
replies[replyIndex] = reply
if reply.repliesIDs != nil {
self.fetchReplies(for: reply) { replies in
replies[replyIndex].replies = replies
}
}
By the way,
- dispatch group must not be a property, but rather must be a local var (especially as you appear to be calling this method recursively!);
- you have several paths of execution where you are not leaving the group (if
data
wasnil
or ifreply.repliesIDs
wasnil
or if JSON parsing failed); and - you have paths of execution where you leave the group prematurely (if
reply.repliesIDs
was notnil
, you must move theleave()
call into that completion handler closure).
I have not tested it out, but I would suggest something like:
private func fetchReplies(for comment: Comment, completionHandler: @escaping ([Comment?]) -> Void) {
var replies = [Comment?](repeating: nil, count: comment.repliesIDs!.count)
let group = DispatchGroup() // local var
for (replyIndex, replyID) in comment.repliesIDs!.enumerated() {
let replyURL = URL(string: "https://hacker-news.firebaseio.com/v0/item/\(replyID).json")!
group.enter()
URLSession.shared.dataTask(with: replyURL) { data, _, _ in
guard let data = data else {
group.leave() // leave on failure, too
return
}
do {
let reply = try self.jsonDecoder.decode(Comment.self, from: data)
replies[replyIndex] = reply
if reply.repliesIDs != nil {
self.fetchReplies(for: reply) { replies in
replies[replyIndex].replies = replies
group.leave() // if reply.replieIDs was not nil, we must not `leave` until this is done
}
} else {
group.leave() // leave if reply.repliesIDs was nil
}
} catch {
group.leave() // leave on failure, too
print(error)
}
}.resume()
}
dispatchGroup.notify(queue: .main) { // do this on main to avoid synchronization headaches
completionHandler(replies)
}
}
Structs that refer to each other in Swift 3
The problem is that an Optional
stores its Wrapped
value inline (see Mike Ash's fantastic blog post for more info about this) – meaning that an Optional
instance (regardless of whether it is nil
or not) will occupy at least the same amount of memory as the type you wish to store in its .some
case (the Wrapped
type).
Thus, as your Pin
struct has a property of type DetailedPin?
, and DetailedPin
has a property of type Pin?
, infinite storage would be required in order to store these values inline.
The solution therefore is simply to add a layer of indirection. One way of doing this would be to make Pin
and/or DetailedPin
a reference type (i.e a class
) as @dfri has suggested.
However, if you wish to keep the value semantics of Pin
and DetailedPin
, one option would be to create a wrapper type backed by a class instance to provide the necessary indirection:
/// Provides indirection for a given instance.
/// For value types, value semantics are preserved.
struct Indirect<T> {
// Class wrapper to provide the actual indirection.
private final class Wrapper {
var value: T
init(_ value: T) {
self.value = value
}
}
private var wrapper: Wrapper
init(_ value: T) {
wrapper = Wrapper(value)
}
var value: T {
get {
return wrapper.value
}
set {
// Upon mutation of value, if the wrapper class instance is unique,
// mutate the underlying value directly.
// Otherwise, create a new instance.
if isKnownUniquelyReferenced(&wrapper) {
wrapper.value = newValue
} else {
wrapper = Wrapper(newValue)
}
}
}
}
You can now just use the Indirect
wrapper for one (or both) of your structs properties:
struct DetailedPin {
private var _pin = Indirect<Pin?>(nil)
// Convenience computed property to avoid having to say ".value" everywhere.
var pin: Pin? {
get { return _pin.value }
set { _pin.value = newValue }
}
}
struct Pin {
var detailedPin: DetailedPin?
var foo: String
}
var d = DetailedPin()
var p = Pin(detailedPin: d, foo: "foo")
d.pin = p
// testing that value semantics are preserved...
var d1 = d
d1.pin?.foo = "bar"
print(d.pin?.foo as Any) // Optional("foo")
print(d1.pin?.foo as Any) // Optional("bar")
Related Topics
How to Convert Timeinterval into Minutes, Seconds and Milliseconds in Swift
How to Override Trait Collection for Initial Uiviewcontroller? (With Storyboard)
How to Get Directory Size with Swift on Os X
Class 'Viewcontroller' Has No Initializers in Swift
Convert Avaudiopcmbuffer to Nsdata and Back
Hide/Show Tab Bar When Push/Back. Swift
How to Get Rgb Components from Color in Swiftui
Is There a Zip Function to Create Tuples with More Than 2 Elements
Using Foreach Loop with Binding Causes Index Out of Range When Array Shrinks (Swiftui)
Expected to Decode Array<Any> But Found a Dictionary Instead
Why Is Manually Setup Root View Controller Showing Black Screen
Add "For In" Support to Iterate Over Swift Custom Classes