Swift Struct с Lazy, частная собственность, соответствующая протоколу

Во-первых, у меня есть протокол, который определяет только несколько свойств readonly, например:

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

Затем я хочу создать структуру, соответствующую этому протоколу. Проблема, с которой я сталкиваюсь, заключается в том, что у меня есть два противоречивых требования:

  • Свойства должны создаваться лениво.
  • Свойства связаны и должны быть сгенерированы вместе.

Я не могу найти способ сделать это. Самое близкое, что я пришел, это примерно так:

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
  }
}

Проблема в том, что я получаю сообщение об ошибке: Immutable value of type 'AStruct' only has mutating members named 'data'.

Кто-нибудь знает, каким образом я смогу выполнить свою цель? Технически переменная data изменена, но никогда не изменится. Я не могу использовать let с lazy, поэтому я не могу указать, что значение никогда не изменится после его создания. Мне нужны значения, которые нужно создать, потому что структура создается в основном потоке, но значения будут генерироваться в фоновом потоке другим процессом в целом.

Update

Поэтому мне было указано, что я могу сделать геттеры mutating как в протоколе, так и в структуре. Это работает, за исключением того, что у меня теперь есть проблема, что я не могу использовать эту структуру в любой другой структуре (которой я являюсь). Итак, в конце концов, я отбросил проблему в другую структуру, которую я не хочу изменять.

Пример:

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

Я не могу получить доступ к переменным в AStruct из Container, потому что Container является неизменным, а переменные-члены AStruct мутируют. Попытка доступа к ним дает мне то же сообщение об ошибке, о котором я говорил ранее.

Изменение контейнера для использования var вместо let дает ту же ошибку:

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

Если я настроил функцию в классе обработки, которая получает Container для обработки:

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

Я получаю ту же ошибку: Immutable value of type 'AStruct' only has mutating members names 'sql'.

Ответ 1

Поскольку доступ к ленивой переменной data мутирует AStruct, любой доступ к ней должен быть помечен как также мутирующий struct. Поэтому вам нужно написать:

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
        }
    }
}

Однако теперь вы обнаружите, что Swift жалуется, что вы не соответствуете Example, потому что его get var1 не помечен как мутирующий. Поэтому вам придется изменить его, чтобы он соответствовал:

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

Ответ 2

Итак, я хочу подробно изложить решение, в котором я закончил. Как оказалось, я не думаю, что в Swift сейчас возможно то, что я хочу. После того, как вы начнете по дороге mutating get, она попадет в слишком много областей (все контейнерные структуры должны быть мутированы и т.д.). В конце концов, этот вид разрушил всю причину, по которой я хотел использовать структуры в первую очередь.

Возможно, по дороге Apple добавит lazy let var = .... Это обеспечило бы неизменность, гарантируя, что ленивая переменная задается только один раз... просто не сразу.

Итак, мое решение состояло в том, чтобы просто отказаться от structs и использовать classes. Я держу классы функционально неизменными, поэтому я все еще сохраняю это. Вполне буквально все, что мне нужно было сделать, это изменить struct на class, и теперь ленивая конструкция работает отлично, и все мои проблемы уходят... кроме того, что я использую класс.

Все, что сказал, @AirspeedVelocity имеет правильное решение, даже если оно несостоятельно для моих нужд, поэтому я буду принимать его решение. Я просто оставляю это здесь, чтобы другие могли понять, как я преодолел проблему... использовать классы.