Unable to update a passed struct, even tho it's declared as var

I am passing a structure into a method. Everything is defined as var, yet within the method where I am just changing one of it's properties,
Code Block
BackGroundStruct.h = 0.1

I get the following error :

Cannot assign to property: 'BackGroundStruct' is a 'let' constant

I purposely set everything to var and am unsure as to what is going wrong here. Also please excuse the sloppy variable naming here.



Code Block
/* structure */
struct backGroundStruct
{
    var w : CGFloat
    var h : CGFloat
    var bGSprite : SKSpriteNode
}
/*method*/
func heresJohnny (BackGroundStruct : backGroundStruct)
{
        BackGroundStruct.h = 0.1
}
/* declare backGroundstruct */
var wnh : backGroundStruct
/* create background and pass it to struct init*/
var background = SKSpriteNode(imageNamed: "background")
background.size = self.size
wnh = backGroundStruct.init(w: background.size.width, h: background.size.height, bGSprite: background)
/* call method */
pegBaseBoard.heresJohnny(BackGroundStruct : wnh)



Answered by DelawareMathGuy in 620874022
hi,

when you pass a struct as an argument, you get a read-only copy of the struct as the argument, as if it were a let constant. so your (slimmed-down, let's avoid the SpriteNode stuff) code is basically this:
Code Block
struct backGroundStruct
{
var w : CGFloat
var h : CGFloat
}
func heresJohnny (BackGroundStruct : backGroundStruct)
{
BackGroundStruct.h = 0.1
}
var wnh : backGroundStruct
wnh = backGroundStruct.init(w: 10, h: 10)
heresJohnny(BackGroundStruct : wnh)

and this is essentially what your function call is doing (which generates the same error trying to modify the argument):
Code Block
var wnh : backGroundStruct
wnh = backGroundStruct.init(w: 10, h: 10)
let argument = wnh // a read-only copy to be passed
argument.h = 0.1 // what the function is doing in heresJohnny(BackGroundStruct : wnh)

the wnh variable would not be changed in the code above.

if you really want your function to work the way you thought it should -- modifying what is passed in -- you'd have to make it an inout argument and pass it as such:
Code Block
func heresJohnny (BackGroundStruct : inout backGroundStruct)
{
BackGroundStruct.h = 0.1
}
var wnh : backGroundStruct
wnh = backGroundStruct.init(w: 10, h: 10)
heresJohnny(BackGroundStruct : &wnh)

the wnh variable has been changed in the code above.

one suggestion: by convention, Swift code usually is written with struct type names in UpperCamelCase and variables in lowerCamelCase. your backGroundStruct and BackGroundStruct name choices run somewhat counter to that convention.

hope that helps,
DMG
Accepted Answer
hi,

when you pass a struct as an argument, you get a read-only copy of the struct as the argument, as if it were a let constant. so your (slimmed-down, let's avoid the SpriteNode stuff) code is basically this:
Code Block
struct backGroundStruct
{
var w : CGFloat
var h : CGFloat
}
func heresJohnny (BackGroundStruct : backGroundStruct)
{
BackGroundStruct.h = 0.1
}
var wnh : backGroundStruct
wnh = backGroundStruct.init(w: 10, h: 10)
heresJohnny(BackGroundStruct : wnh)

and this is essentially what your function call is doing (which generates the same error trying to modify the argument):
Code Block
var wnh : backGroundStruct
wnh = backGroundStruct.init(w: 10, h: 10)
let argument = wnh // a read-only copy to be passed
argument.h = 0.1 // what the function is doing in heresJohnny(BackGroundStruct : wnh)

the wnh variable would not be changed in the code above.

if you really want your function to work the way you thought it should -- modifying what is passed in -- you'd have to make it an inout argument and pass it as such:
Code Block
func heresJohnny (BackGroundStruct : inout backGroundStruct)
{
BackGroundStruct.h = 0.1
}
var wnh : backGroundStruct
wnh = backGroundStruct.init(w: 10, h: 10)
heresJohnny(BackGroundStruct : &wnh)

the wnh variable has been changed in the code above.

one suggestion: by convention, Swift code usually is written with struct type names in UpperCamelCase and variables in lowerCamelCase. your backGroundStruct and BackGroundStruct name choices run somewhat counter to that convention.

hope that helps,
DMG
I found  DelawareMathGuy's answer was already there after I hit Submit. The core part of this answer is just a duplicate of his. But I cannot delete this answer...


What is self? You should better show more context.
And in Swift, only type names start with Capital letter.
(Do you have some experience with other programming languages? But now you use Swift. Do in Swifty way when in Swift.)

I have fixed the capitalization issue and guessed the context as follows:
Code Block
/* structure */
struct BackGroundStruct {
var w : CGFloat
var h : CGFloat
var bGSprite : SKSpriteNode
}
class PegBaseBoard: SKSpriteNode {
//...
/*method*/
func heresJohnny(backGroundStruct: BackGroundStruct) {
backGroundStruct.h = 0.1
}
//...
}
class GameScene: SKScene {
//...
override func didMove(to view: SKView) {
//...
let pegBaseBoard = PegBaseBoard(/*...*/)
/* declare backGroundstruct */
var wnh: BackGroundStruct
/* create background and pass it to struct init*/
let background = SKSpriteNode(imageNamed: "background") //Use `let` here
background.size = self.size
wnh = BackGroundStruct(w: background.size.width, h: background.size.height, bGSprite: background)
/* call method */
pegBaseBoard.heresJohnny(backGroundStruct: wnh)
//...
}
//...
}

If my guesses are wrong, please tell me as soon as possible. Exactly the same code might not work when placed in different contexts.


In Swift, struct is a value type, and you need to make the container (variable) of the struct mutable (using var) to modify the member of the struct.
Seems you understand this part.

And in Swift, also the usual formal parameters are implicitly declared as let, not var.

Thus, you cannot modify properties (or call mutating methods) of some formal parameter of struct type.


Declare the formal parameter using inout:
Code Block
/*method*/
func heresJohnny(backGroundStruct: inout BackGroundStruct) { //<- Declare the formal parameter using `inout`
backGroundStruct.h = 0.1 //<- This does not cause error
}

When calling, prefix & on the actual parameter passed to inout
Code Block
pegBaseBoard.heresJohnny(backGroundStruct: &wnh) //<-Use `&` to pass actual parameters to `inout`



With the changes above, the line backGroundStruct.h = 0.1 would not cause compile-time error.
If you find another problem with this fix, please start a new thread for it.


Thank you, so we're talking reference pointers here (finally something I recognize), and yes, my code gets sloppier the more walls I hit. I barely transitioned from C to an Objective language, and at my age I wonder if I am capable of learning anything new.

Thanks again

Although as a side note, when I watched it change to 0.1 in the debugger, it actually changed to:

0.10000000000000001
What's up with that?

Unable to update a passed struct, even tho it's declared as var
 
 
Q