Swift Struct with Lazy, private property conforming to Protocol

ぐ巨炮叔叔 提交于 2019-12-22 03:51:20

问题


First, I have a protocol that defines only a few, readonly properties, ex:

protocol Example {
  var var1:String { get }
  var varArray:[String] { get }
}

I then want to create a struct that conforms to that protocol. The problem I'm running into, is that I have two conflicting requirements:

  1. The properties need to be lazily generated.
  2. The properties are related and need to be generated together.

I can't seem to find a way to do this. The closest I've come is something like this:

struct AStruct : Example {
  private lazy var data:(var1:String, varArray:[String]) = {
    var stringValue:String = ""
    var stringArray:[String] = []
    //Generate data
    return (stringValue, stringArray)
  }()

  var var1:String {
    return self.data.var1
  }

  var varArray:[String] {
    return self.data.varArray
  }
}

The problem is, I'm getting the error: Immutable value of type 'AStruct' only has mutating members named 'data'.

Does anyone know of a way I might be able to accomplish my goal? Technically, the data variable is mutable but will never change. I can't use let with lazy so I can't specify that the value will never change once it's been generated. I need the values to be generated though, because the struct is created on the main thread, but the values will be generated on a background thread by another process altogether.

Update

So it was pointed out to me that I can make the getters mutating in both the protocol and the struct. That works, except I now have the problem that I cannot use this struct in any other struct (which I am). So in the end, I've pushed off the problem into another struct, which I do not want to be mutable.

Ex:

struct Container {
  let astruct:AStruct
  let callback:() -> ()
}

I cannot access the variables in AStruct from Container because Container is immutable and the member variables of AStruct are mutating. Trying to access them gives me the same error message I spoke of earlier.

Changing the container to use var instead of let yields the same error:

struct Container {
  var astruct:AStruct
  let callback:() -> ()
}

If I set up a function in the processing class that receives a Container to process:

func processContainer(cont:Container){
  self.doSomething(cont.astruct.var1)
}

I get the same error: Immutable value of type 'AStruct' only has mutating members names 'sql'.


回答1:


Because accessing the lazy data variable mutates AStruct, any access to it must be marked as also mutating the struct. So you need to write:

struct AStruct : Example {
    // ...
    var var1: String {
        // must declare get explicitly, and make it mutating:
        mutating get {
            // act of looking at data mutates AStruct (by possibly initializing data)
            return self.data.var1
        }
    }

    var varArray:[String] {
        mutating get {
            return self.data.varArray
        }
    }
}

However, you'll find now that Swift complains you aren't conforming to Example, because its get var1 isn't marked as mutating. So you'd have to change it to match:

protocol Example {
    var var1:String { mutating get }
    var varArray:[String] { mutating get }
}



回答2:


So I want to detail out the solution I ended up following. As it turns out, I don't think what I want is currently possible in Swift. Once you start down the mutating get road, it ends up cascading into too many areas (all container structs need to be mutating, etc). In the end, this kind of ruined the whole reason I wanted to use structs in the first place.

Perhaps down the road Apple will add a lazy let var = .... This would ensure immutability by guaranteeing the lazy variable is only ever set once... just not immediately.

So my solution was simply to forego structs altogether and use classes instead. I keep the classes functionally immutable, so I still retain that. Quite literally, all I had to do was change struct to class and now the lazy construction works perfectly and all my problems go away... except I'm using a class.

All that said, @AirspeedVelocity has a correct solution, even if untenable for my needs, so I'll be accepting his solution. I'm just leaving this here so others can understand how I overcame the problem... use classes.



来源:https://stackoverflow.com/questions/28202834/swift-struct-with-lazy-private-property-conforming-to-protocol

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!