Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy let? Unswifty workaround in structs

I have an immutable struct that I'm working with, and I'm trying to create a property that is not a computed value, however it's assignation requires the values of previously assigned properties.

(Wow mouthful).

The property I'm struggling with is perimeter: Int

// Floor made out of square tiles.
struct Floor { 

  let
  size:           (x: Int, y: Int), // How many tiles in the floor
  tilePerimeter:  Int,

  // Calculate entire floor's perimeter based on tile quantity and perimeter:
  lazy let perimeter: Int = {

    let
    t4th  = self.tilePerimeter / 4,  // Tile width / length
    sx    = self.size.x * t4th,      // All X tiles' length
    sy    = self.size.y * t4th       // All Y tiles' width

    return (sx * 2) + (sy * 2)       // Perimeter calc
  }()
};

Unfortunately, Swift will not let me use a lazy let.. So my workaround here was to just make a computed var with only a getter... but if this calculation took a long time (loading an image or complex math) then it would be called many times, when really it is only needed to call once

So my actual workaround was to just to create an initializer and assign the perimeter there... only some of my structs have MANY properties, and not creating an initializer for structs is part of the Swiftiness that I love.

I know that I could just make this a lazy var and instantiate the struct as let, but that seems confusing and error-prone.

Is there a way to do this where I can retain immutability, performance, and Swiftiness? And also, what is the reason for there not being allowed to use lazy let?

like image 247
Fluidity Avatar asked Oct 20 '25 01:10

Fluidity


2 Answers

Maybe you could use lazy var after all:

lazy private(set) var perimeter: Int = {
    ...
}()

A read-only var gets you closer to the desired let semantics.

like image 181
Paulo Mattos Avatar answered Oct 21 '25 16:10

Paulo Mattos


You can force this to be computed only once while still using a read-only computed var by using an optional private var with a default of nil, much in the same way as singleton objects are implemented.

// Floor made out of square tiles.
struct Floor {
  private var computedPerimeter: Int? = nil

  let
  size:           (x: Int, y: Int), // How many tiles in the floor
  tilePerimeter:  Int

  // Calculate entire floor's perimeter based on tile quantity and perimeter:
  var perimeter: Int {
    mutating get {
        if computedPerimeter == nil {
            let
            t4th  = self.tilePerimeter / 4,  // Tile width / length
            sx    = self.size.x * t4th,      // All X tiles' length
            sy    = self.size.y * t4th       // All Y tiles' width

            computedPerimeter = ((sx * 2) + (sy * 2))       // Perimeter calc
         }
         return computedPerimeter!
     }
  }
};
like image 26
Pedro Castilho Avatar answered Oct 21 '25 16:10

Pedro Castilho



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!