Reference variable in another variables initialisation

I'm trying to reference a variable inside the initialisation of another variable, mostly because it would make the code easier to manage over time as the value may change so it just seems cleaner.

let mySize:Int = 72

let coords:[(id:Int, x:Int, y:Int)] = [
    (1, mySize - (mySize * 2), mySize - (mySize * 2)),
    (2, 0, mySize - (mySize * 2)),
    (3, mySize, mySize - (mySize * 2)),
]

I'm getting this error:

Cannot use instance member 'mySize' within property initializer; property initializers run before 'self' is available

The idea being that if I update the mySize variable I don't have to change all of the coordinates to account for the change in size.

I could just hardcode the numbers but it seems inefficient.

1 Like

You didn't ask an actual question, so it's not 100% clear what kind of answer you're looking for. If you're asking what you can do instead, there are 2 easy options:

  1. Start with static let mySize:Int = 72. The precise form of the initializer on coords will depend on whether you want to make it static too. There's not a lot of difference between let and static let.

  2. Start with let mySize:Int and let coords:[(id:Int, x:Int, y:Int)] (so: no initializer expressions) and set the values of both variables in the type's initializer method. The compiler should be able to figure out that it can initialize both variables statically regardless of where the initializer expressions are placed in source code.

It's a personal thing how satisfied you might be with such alternatives. You might think it "unclean" to separate the initial value from the declaration site, but others might think it cleaner to consolidate several initializations into one place in the initializer method, so :person_shrugging: .

If you search the forums, you should be able to find discussions about adding some kind of "const" declaration into Swift, which might end up a lot closer to what you originally had in mind. But the language doesn't have that yet.

Or, you might be able to use macros to figure out how to implement something that feels more natural to you for this kind of pattern.

1 Like
struct MyStruct {
    let mySize = 72
    let coords: [(id: Int, x: Int, y: Int)]
    
    init() {
        self.coords = [
            (id: 1, x: mySize - (mySize * 2), y: mySize - (mySize * 2)),
            (id: 2, x:                     0, y: mySize - (mySize * 2)),
            (id: 3, x:                mySize, y: mySize - (mySize * 2)),
        ]
    }
}

Side note, mySize - (mySize * 2) is just -mySize, so you could simplify that a lot:

struct MyStruct {
    let mySize = 72
    let coords: [(id: Int, x: Int, y: Int)]
    
    init() {      
        self.coords = [
            (id: 1, x: -mySize, y: -mySize),
            (id: 2, x:       0, y: -mySize),
            (id: 3, x: +mySize, y: -mySize),
        ]
    }
}
5 Likes

This is exactly what I as after and works perfectly.

Thank you.

Also if mySize is always 72, you can make it into a static let or move it to the top-level scope to avoid storing it inside every instance of MyStruct.

1 Like

Yet another option (albeit not perfect as it changes a let for a var) is to make coords a lazy var.

2 Likes