38

I was browsing the docs, and I found StaticString. It states:

An simple string designed to represent text that is "knowable at compile-time".

I originally thought that String has the same behaviour as NSString, which is known at compile time, but it looks like that I was wrong. So my question is when should we use StaticString instead of a String, and is the only difference is that StaticString is known at compile-time?

One thing I found is

var a: String = "asdf" //"asdf"
var b: StaticString = "adsf" //{(Opaque Value), (Opaque Value), (Opaque Value)}

sizeofValue(a)  //24
sizeofValue(b)  //17

So it looks like StaticString has a little bit less memory footprint.

2 Answers 2

17

It appears that StaticString can hold string literals. You can't assign a variable of type String to it, and it can't be mutated (with +=, for example).

"Knowable at compile time" doesn't mean that the value held by the variable will be determined at compile time, just that any value assigned to it is known at compile time.

Consider this example which does work:

var str: StaticString

for _ in 1...10 {
    switch arc4random_uniform(3) {
    case 0: str = "zero"
    case 1: str = "one"
    case 2: str = "two"
    default: str = "default"
    }
    print(str)
}

Any time you can give Swift more information about how a variable is to be used, it can optimize the code using it. By restricting a variable to StaticString, Swift knows the variable won't be mutated so it might be able to store it more efficiently, or access the individual characters more efficiently.

In fact, StaticString could be implemented with just an address pointer and a length. The address it points to is just the place in the static code where the string is defined. A StaticString doesn't need to be reference counted since it doesn't (need to) exist in the heap. It is neither allocated nor deallocated, so no reference count is needed.

"Knowable at compile time" is pretty strict. Even this doesn't work:

let str: StaticString = "hello " + "world"

which fails with error:

error: 'String' is not convertible to 'StaticString'

Sign up to request clarification or add additional context in comments.

2 Comments

I'm a little confused about "the value held by the variable will be determined at compile time, just that any value assigned to it is knowable at compile time." What is then the difference between knowable at compile time and determined at in compile time? And StaticStrings cannot be mutated, however they can be reassigned as well. So basically the compiler cannot be sure about its value.
The compiler doesn't know which value a variable of type StaticString will hold at compile time, but it does know that any value that will be assigned to that variable is a string literal that was known at compile time. What Swift will do with that information is known only to the authors of Swift until they release the open source code.
9

StaticString is knowable at compile time. This can lead to optimizations. Example:

EDIT: This part doesn't work, see edit below

Suppose you have a function that calculates an Int for some String values for some constants that you define at compile time.

let someString = "Test"
let otherString = "Hello there"

func numberForString(string: String) -> Int {
    return string.stringValue.unicodeScalars.reduce(0) { $0 * 1 << 8 + Int($1.value) }
}

let some = numberForString(someString)
let other = numberForString(otherString)

Like this, the function would be executed with "Test" and "Hello there" when it really gets called in the program, when the app starts for example. Definitely at runtime. However if you change your function to take a StaticString

func numberForString(string: StaticString) -> Int {
    return string.stringValue.unicodeScalars.reduce(0) { $0 * 1 << 8 + Int($1.value) }
}

the compiler knows that the passed in StaticString is knowable at compile time, so guess what it does? It runs the function right at compile time (How awesome is that!). I once read an article about that, the author inspected the generated assembly and he actually found the already computed numbers.

As you can see this can be useful in some cases like the one mentioned, to not decrease runtime performance for stuff that can be done at compile time.

EDIT: Dániel Nagy and me had a conversation. The above example of mine doesn't work because the function stringValue of StaticString can't be known at compile time (because it returns a String). Here is a better example:

func countStatic(string: StaticString) -> Int {
    return string.byteSize    // Breakpoint here
}

func count(string: String) -> Int {
    return string.characters.count    // Breakpoint here
}

let staticString : StaticString = "static string"
let string : String = "string"


print(countStatic(staticString))
print(count(string))

In a release build only the second breakpoint gets triggered whereas if you change the first function to

func countStatic(string: StaticString) -> Int {
    return string.stringValue.characters.count    // Breakpoint here
}

both breakpoints get triggered.

Apparently there are some methods which can be done at compile time while other can't. I wonder how the compiler figures this out actually.

8 Comments

So basically when I'm debugging it and put a breakpoint in the function which uses the StaticString, it shouldn't even enter it?
@DánielNagy Hmm I don't know, maybe it gets caught while compiling, you can try it out I guess
I tried it, and you were right! But can you complete your answer with a small addition? I used this: func test() -> String { return StaticString(stringLiteral: "hejj").stringValue } method to test what you claimed, put a breakpoint to there, and see what is happening. The default Swift compiler optimization is None, but if you change that to Fastest or Fastest, Unchecked, the compiled value got inserted, so the program didn't stopped at the breakpont. So can you add these Swift compiler optimization changes to your answer?
@DánielNagy Hmm I can't verify this, for me it stops at the breakpoint no matter what, release/debug build. Did you maybe uncheck "Debug executable"?
Where do I find that option?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.