Static computed property vs static let constant?

This has probably been asked before, perhaps even by myself but I couldn't find it so:

What are the differences between c and d here:

struct S {
    static let c: Int = 1234
    static var d: Int { return 1234 }
}

?

1 Like

What exactly do you mean by 'difference'? I'd say there is no difference in your particular example, but if you had Int.random() there instead then d will always return new values on each call.

If you meant to ask what this compiles to at low level, then I cannot answer that question.

1 Like

For a proof that there's no difference in this specific case see https://godbolt.org/z/6iLNSc
Both compile to the same code when optimizations are enabled.


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

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

Any difference at all, given the particular example (a constant, same return value every time it is called), so a very similar question is eg:

Why is Float.pi declared as a static computed property and not a static let constant?

Note that a protocol requirement like static var pi: Self { get } can be satisfied both by a static let constant and a static computed property.

Thanks! So how come the static-computed-property-way seems to be preferred in eg the standard library? It's not the shortest to write/read.

1 Like

That is because it was probably easier to write a gyb file this way. I don't see any other reason for not making it a constant:

After optimisations, there's going to be no difference. Without optimisations, c's getter will use an unsafeMutableAddressor which would return a pointer to the variable's storage and use it to return the stored value.

Instead of returning a pre-defined value, if this was returning an instance of something (say, a UIView), then yes, there would actually be a difference.

1 Like

So eg this:

extension Point {
    static let zero = Point(0, 0)
}

should be preferred over this:

extension Point {
    static var zero: Point { return Point(0, 0) }
}

?

In a normal library, a let is an additional promise that the value will be the same every time, which might be important if the compiler can't see the body of the function. On the other hand, if the computation of the value has side effects, the compiler has to take care that it's only computed once.

In a "resilient" library (like the stdlib and overlays), let doesn't promise that outside of the library itself, so that it's a "safe" change to go from let to a computed var if necessary.

4 Likes

From the Swift users perspective I would say it's a matter of taste. I personally would prefer the former if I don't need a class variable for example:

extension UITableViewCell {
  class var identifier: String {
    return String(describing: self)
  }
}

First is returning Point(0, 0) which is stored in zero variable's storage. This uses unsafeMutableAddressor as I mentioned above.

The second is creating a new instance of Point(0, 0) each time.

Again, this difference only exists without optimisations enabled. After optimisations, both generate the same SIL.

Yes, unless it's a reference type that's being returned of course:

class C {
    init() {}
}

struct S {
    static let e: C = C()
    static var f: C { return C() }
}

print(S.e === S.e) // true
print(S.f === S.f) // false

EDIT:

And also, non-reference type cases like this:

struct R {
    let v: Double
    init() { self.v = Double.random(in: 0 ..< 1) }
}

struct S {
    static let g: R = R()
    static var h: R { return R() }
}

print(S.g.v == S.g.v) // true
print(S.h.v == S.h.v) // false
1 Like

That's correct

Nope:

struct A { static let a:Int = 89 }
struct B { static var b:Int { return 89 } }

print(MemoryLayout<A>.size, MemoryLayout<B>.size) // 0 0
2 Likes

oh i completely misread the title of this thread lol

1 Like

Isn't let also a promise that the value is computed only once?

Does the stdlib use a special modifier to not promise this; is there a precedent?

That's the point. let makes that promise, a computed var does not. Sometimes that promise is trivial to keep, but sometimes it means an extra runtime check.

It's part of being a resilient library, i.e. one that's expected to evolve over time while keeping the same ABI. We're hoping to talk more about making that a general feature within the Swift 5.1 timeframe, but the short answer is "-enable-resilience". That has a lot more effects besides just treating let as if it might change, though.

2 Likes

Ah, sorry, I was under the impression the talk was about an allowance to override lets iff they come from resilient libraries.

Terms of Service

Privacy Policy

Cookie Policy