Hey all!
This came up during a discussion today and I honestly wasn't sure if my assumptions were correct so looking for some clarity here!
What are the compile-time performance differences between the four declarations below?
(I have added how I think it works (based on reading the Type Inference document) in comments along with my doubts.)
let a = "hello, world!" // type is inferred
let b = String("hello, world!") // type is inferred from String(...) and then passed to the root (the constant b)
let c: String = .init("hello, world!") // type inference is not required
let d: String = "hello, world!" // type inference is not required
Assumptions: type inference adds a small compile-time performance penalty
Note: I searched this forum and few other websites for related discussions, including the Swift reference, but couldn't find anything conclusive
Edit 0: Added "compile-time" to the word "performance" to avoid any confusion
Hi! There is no runtime penalty, AFAIK. There might be some minuscule compile-time difference but I wouldn't worry about it, honestly. So then it just comes down to a question of style. The style guide that I use prefers a.
This is in line with my experience. I removed all .init(…)s from a project that had lots of them because they took up so much of the total compile time.
I think the big difference would be building with assertions, which I'm pretty sure are disabled for the toolchain builds. But Apple builds Xcode's default toolchain internally, so I'm not sure if there's any difference there.
In the past there was no difference, but today (after SE-0213) String("…") is equivalent to "..." as String which creates String directly from the literal.
The String.init("…") first makes a String from a literal, and then calls copy init on it.
Correct, “c” and “e” actually have different user-facing semantics from the remaining examples. They differ in more than just code style from the rest, but in fact have different behavior.
The other cases differ by the presence/absence or location of the type annotation, and all perform rather similarly.
By contrast, “c” and “e” (that is, writing out .init with a type that is expressible by a literal) explicitly tells the compiler that you want first to create an instance of, if possible, the default literal type and then to convert it via an unlabeled initializer to the type you specify.
Looking through all unlabeled initializers defined on String and figuring out which is the best candidate among those that can take a single argument of a type that is expressible by a string literal is a chunk more work. SE-0213 explicitly preserves the .init spelling as the escape hatch to request this behavior, which used to apply to the String("…") spelling as well that is now special-cased to be a synonym of "…" as String.
It's definitely issue. Weren't there assertion disabled toolchains available early on once toolchains started becoming available? I seem to remember there being two downloads. In any case, I can do a build and test it out if you can give me the command.
Although it's understandable while there is a performance difference between different expressions as others already mentioned it's concerning to me that there is an overall difference between releases for such simple expressions, I suspect it's property wrappers which contributed here so PR 37801 might have improved that.