Assign a variable a value once then make it read only

(Thoughtcastdavid) #1

Hi. I'm trying to create a variable that is a constant but I can't define the constant before super.init. So my question is this, is there a way to create a variable in my classes scope that becomes read only after I set a value?

as it stands, i know i can use "let myVariable = xxxx" - however, i need myVariable to be accesible through my entire project, not just one function.

to sum up my question in pseudo code...

let myVar: VALUETYPE


myFunction()
{
myVar = "value" // after this is set I want myVar to be read-only

}

myFunctionTwo()
{
print(myVar)
}
(Jeremy David Giesbrecht) #2

I am unaware of any direct language support.

I think the best you can do is reduce the access level of its actual storage and vend it through a forwarding property that has only a getter and no setter:

private var _myVar: VALUETYPE?
public var myVar: VALUETYPE {
    guard let defined = _myVar else {
        preconditionFailure("Forgot to call “myFunction()” first.")
    }
    return defined
}
public func myFunction() {
    precondition(_myVar == nil, "“myFunction()” called a second time.")
    _myVar = "value"
}
(Alexander Momchilov) #3

I would wrap that pattern up into its own type:

// Warning: not thread safe, but if you're using this sort of initialization
// in a threaded context, you probably want to rethink your design anyway.
struct SingleSettable<T> {
	private var _value: T?
	
	public var value: T {
		get { 
			guard let defined = _value else {
				preconditionFailure("Forgot to set the value of this \(type(of: self))")
			}
			return defined
		}
		set {
			precondition(_value == nil, "A \(type(of: self))'s value was set a second time")
			_value = newValue
		}
	}
}

struct SomeTwoStageInitializedType {
	let a, b, c: Int
	var _d = SingleSettable<Int>()
	var d: Int {
		get { return _d.value }
		set { _d.value = newValue }
	}

	init(a: Int, b: Int, c: Int) {
		(self.a, self.b, self.c) = (a, b, c)
	}
}

var x = SomeTwoStageInitializedType(a: 1, b: 2, c: 3)
//print(x.d) // Boom
x.d = 4
//x.d = 5 // Boom

I wish there was a less verbose to write:

var d: Int {
	get { return _d.value }
	set { _d.value = newValue }
}

Something like:

var d: fowarding to self._d
(Jeremy David Giesbrecht) #4