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 }
}
?
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 }
}
?
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.
For a proof that there's no difference in this specific case see Compiler Explorer
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
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.
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.
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.
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
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
oh i completely misread the title of this thread lol
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.
Ah, sorry, I was under the impression the talk was about an allowance to override let
s iff they come from resilient libraries.
There's a difference and could make a huge Impact. In case the static property depends on a result of a function and we used static let, It will call the function one time on the first time and save Its value as long the property is alive.
Example: If we used localization in the app and a static variable would retrieve a localized value, If used static let It would call It once, In this case, we could change the language but the variable still refers to the old value. So the solution here should use Computed static
public enum NyEnum {
public static let intField1: Int = 1
public static var intField2: Int { 1 }
}
I am using a -O optimization flag.
I have two static props let and var.
They have the same assembly instruction except the unsafeMutableAddressor
part.
Why do the swift compiler generate an unsafeMutableAddressor
, for primitive types like Int.