Something odd about protocol get-only static property: why is it `var`, not `let`, and very surprising: it can be re-assigned!

// somethiong similar to SwiftUI.PreferenceKey
protocol SimpleProtocol {
    associatedtype Value

    static var value: Value { get }
}

struct SomeSimple: SimpleProtocol {
    // 1) why cannot be a `let`, why is `var` used? which lead to some unexpect: I can re-assign to this property!
    static var value = 100
    // so should I be doing this? Is this the proper/correct way?
//    static var value: Int { 100 }
}

// Oh no, can re-assign to a get only value?! Why can I violate the protocol's definition?
SomeSimple.value = 123

print(SomeSimple.value)    // print 123!

So I have two questions: 1) at the protocol implementation, why is a get-only property a var, not a let? Look confusing reading the code. 2) why can I re-assign a get-only property to some different value?

  1. at the protocol implementation, why is a get-only property a var , not a let ?

It absolutely can be a let. Xcode's fixits insert vars in such cases though.

  1. why can I re-assign a get-only property to some different value?

You are accessing value through SomeSimple, not SimpleProtocol. And in the context of SomeSimple, the value is mutable. If you changed it to let, you could not reassign it.

1 Like

1. You can use let in the struct if you want S.value to be immutable.

2. The protocol does not say the property is get-only—in fact there is no way to do that. The protocol simply does not require a setter.

3. I believe I have seen it mentioned that a static computed property is more efficient than a static stored property, but I don’t have a citation at hand.

3 Likes

I found this some time ago:

3 Likes

Ah, my example isn't exactly what got me confused. Value was actually Int?:

struct SomeSimple: SimpleProtocol {
    static var value: Int?
}

That was what I did at first, relying on value getting default of nil, then I tried changing var to let without explicitly assigning nil and the compiler did not like that:

// somethiong similar to SwiftUI.PreferenceKey
protocol SimpleProtocol {
    associatedtype Value

    static var value: Value { get }
}

struct SomeSimple: SimpleProtocol {
    static let value: Int?                 // !! without   = nil
}

print(SomeSimple.value ?? -1)

So I am clear now it can be a let.

But now I want to know: why when value was:

var value: Int?

value is nil automatically.

but if it's:

let value: Int?

I get error:

static var declaration requires an initializer expression or getter/setting specifier

So why a var of optional gets a nil value automatically, but a let do not?

I don't think there's any intentional code that wants let value to default to nil. Since that would count as an initialization, you can't ever change that let to a non-nil value.

There are actually people who want to get rid of the default nil on the var side, but given the src compat, that's quite unlikely.

4 Likes

Oh I see: it's to allow a let to be initialized some time later...

swiftc 5.3

static output.S.c.getter : Swift.Int:
        push    rbp
        mov     rbp, rsp
        mov     rax, qword ptr [rip + (static output.S.c : Swift.Int)]
        pop     rbp
        ret

static output.S.d.getter : Swift.Int:
        push    rbp
        mov     rbp, rsp
        mov     eax, 1234
        pop     rbp
        ret

not the same, 1234 is stored in memory somewhere?

static output.S.c : Swift.Int:
        .quad   1234

swiftc nightly:

static output.S.c.getter : Swift.Int:
        mov     eax, 1234
        ret

static output.S.d.getter : Swift.Int:
        mov     eax, 1234
        ret

become just 2 instructions!

5 Likes

Ooh, is the optimizer finally getting rid of the pointless stack pointer shuffling in leaf functions?