Swift has disabled a feature required for the way i learned to traverse a tree. How is is supposed to be done in swift?

Swift 3, turned every property passed into a function into a Let constant.

This means you can't make changes to a property passed into a function, utterly destroying the approach i take to recursively editing tree data.


How, in Swift are you suppossed to recursivly traverse and update tree data? There aren't any for-loops for this stuff. I used to recursively call the same Function for every object in the hierarchy. Impossible now.

Answered by QuinceyMorris in 230969022

The question is, what kind of type is Node? Is it a reference type or a value type (a class or a struct)?


If it's a reference type, then you can change the properties of the object referenced by myVariable without making it "inout". You'd only need "inout" if you wanted the caller if the reference itself changed.


If it's a value type, you need "inout" to modify the caller's value. However, this may still not be enough, depending what's happening at the calling location. For example, let's say Node is a struct with a "nextNode" property, used to make a chain of nodes, and suppose you have code like this:


var someNode = …
var nextNode = someNode.nextNode
myFunc (&nextNode)


This will mutate the value of nextNode, but leave someNode.nextNode alone, because nextNode is a copy of someNode.nextNode. If you have a chain or tree structure made out of value types, then you need a "trail" of "inout" parameters all the way back to the original structure, and you have to be very careful never to modify a copy.


For that reason, in a case like this, where you want to mutate a tree, it's best to use a reference type (class), and in that case you don't need "inout" in most situations.


If you think something isn't working right (like "inout") you have to post some actual code fragments, including the calling site and the called code. Tell us what you expected to happen, and what did happen, and how you found that out. Waving hands around generalities isn't much help at this point.

Can't you use inout before the parameter name ?

not having any idea what you are asking: no can't.

I've done the google search on this, and the only answer I have found was to put the term 'var' in front of the property name in the function declaration. That causes a compiler error. Is inout something like that?

I found some documentation on How to add inout to a function declaration.

good news: it compiles.

bad news: it appears that I wind up with the same exact problem I had before. It seems that the function makes changes to a local copy of the variable, and throws it away without changing the object in the tree itself.


I need to do more testing to be certain

When you declared a finc with var :

func myFunc(var param: Int) {
}

param could be modified in the scope of the func, but not returned with modified value.


So it was equivalent to :


func myFunc(param: Int) {
var localParam = param
// And then use localParam
// but param is not changed.
}


With inout :

func myFunc(inout param: Int) {
param = 2*param
}


You call :

var input = 3
myFunc(&input)          // don't forget &
print(input) // then input is 6


Look at Swift programming Language for more info on inout


PS: if you search "Swift var parameter for func", you get a lot of references for inout.

This one explains in detail why var was removed in func parameters:

h ttps://github.com/apple/swift-evolution/blob/master/proposals/0003-remove-var-parameters.md

yeah, found that point in the docs. I'm just still getting return values that indicate that the property is not updated.


My code is not straightforward so I have to track it all down, and it could easily be operator error at this point, but so far: I am making a value change to an object (which appears to be the same object in the tree) and then when i check that property, it is not changed at all. I haven't found any instances where Im replacing the value, in effect resetting it... so far it just looks like the object i am making changes to is a copy of the data in the tree.

Yes, with var you make the change on a copy.

With inout, as well, but you write back the new value to the caller.

i think I am misunderstanding...

what is the point of supporting the inout, if you aren't making changes to the original? This is a class, with variables, I should be able to just change the variables without being forced to replace entire sections of the tree with copies, for each individual change.


let's say for a moment that I am working with the root of the entire tree, and I pass it into a function that is configured:

func myFunc(_ myVariable : inout Node)

so i get a copy of that object? not access to the object itself? And when i make changes to it's properties, I have to then replace the entire tree structure? that doesn't sound right.

Do read the Swift programming Language for details on inout.


Most likely, what you learned using var would not have worked with var parameter in Swift language.


It is different in other languages. And inout is there explicitely for this purpose.

I appreciate your trying to help out, I really do.


But, I have found that it just doesn't work as advertized. I read the docs, I followed instructions, I did a test transforming a single entity. That worked just fine.


but when you traverse a tree, going from the base object all the way up, The changes to objects are thrown away when the function completes. I'm watching it happen in the debugger. This approach is either buggy, or it is not what is very specifically spelled out in the docs. I need to find another way.

Accepted Answer

The question is, what kind of type is Node? Is it a reference type or a value type (a class or a struct)?


If it's a reference type, then you can change the properties of the object referenced by myVariable without making it "inout". You'd only need "inout" if you wanted the caller if the reference itself changed.


If it's a value type, you need "inout" to modify the caller's value. However, this may still not be enough, depending what's happening at the calling location. For example, let's say Node is a struct with a "nextNode" property, used to make a chain of nodes, and suppose you have code like this:


var someNode = …
var nextNode = someNode.nextNode
myFunc (&nextNode)


This will mutate the value of nextNode, but leave someNode.nextNode alone, because nextNode is a copy of someNode.nextNode. If you have a chain or tree structure made out of value types, then you need a "trail" of "inout" parameters all the way back to the original structure, and you have to be very careful never to modify a copy.


For that reason, in a case like this, where you want to mutate a tree, it's best to use a reference type (class), and in that case you don't need "inout" in most situations.


If you think something isn't working right (like "inout") you have to post some actual code fragments, including the calling site and the called code. Tell us what you expected to happen, and what did happen, and how you found that out. Waving hands around generalities isn't much help at this point.

QuincyMorris,

thanks for the thoughtful reply.


it's a struct. And I was assuming that it was class (not having looked at the code for the entity for a little while.)

But having had my fill of structs, I think I will convert it to a class, and just see if all of these issues just go away.

thnx

converting to a class, solved the issue. I got rid of all of the inout cruft in the code, and everything works as expected in the first place.

For that reason, in a case like this, where you want to mutate a tree, it's best to use a reference type (class), and in that case you don't need "inout" in most situations.

Alternatively you can copy the tree and make the mutations while doing the copy. This is a commonly-used approach when dealing with recursive structures in a value type world. For a good introduction to the concept, check out Growing Trees with Recursive Enums in Swift.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

WWDC runs Mon, 5 Jun through to Fri, 9 Jun. During that time all of DTS will be at the conference, helping folks out face-to-face. http://developer.apple.com/wwdc/

Thanks for another great external link. It's worth noting, though, that the trees in that presentation are not the same kind of trees we're talking about here. Those trees are binary trees that encode spatial information (or some other kind of metric). The trees here encode parent/child relationships. I think you can encode a descendant (child of child of …) tree using value types, but I don't think you can encode the parent relationships without references (or something equivalent to references).


The other odd thing about the presentation is that it shows how easy it is to get naming wrong. The enum case called ".leaf" did not in fact represent a leaf, but was merely a no-descendent marker. A leaf node was actually represented by a ".node" case with ".leaf" cases for both its decendants.


The other one I noticed was the "median" variable that didn't represent a median value, but the index of the median value in a sorted list. It's not a big deal, but it does make the code read rather oddly. It's a good reminder that if we want our code to be well-readable by others, we may need to go back and re-check the semantics of our variable naming.

Swift has disabled a feature required for the way i learned to traverse a tree. How is is supposed to be done in swift?
 
 
Q