In The Swift Programming Language, in the section on Strings, subsection String Mutability, it says this:
You indicate whether a particular
String
can be modified (or mutated) by assigning it to a variable (in which case it can be modified), or to a constant (in which case it cannot be modified):
and gives example code:
var variableString = "Horse" variableString += " and carriage" // variableString is now "Horse and carriage" let constantString = "Highlander" constantString += " and another Highlander" // this reports a compile-time error - a constant string cannot be modified”
The book in iBooks here, or in a web browser here.
In the next paragraph it claims that "strings are value types".
My question: that doesn't look like a mutable string to me. It looks like what I'm used to in Java (or C#, Python, and others): immutable string objects with mutable variable bindings. In other words, there was an object "Horse" and then it created a new String object "Horse and carriage" and set it to the same variable. And since there is no way to tell the difference between an reference to an immutable object versus a value type (right?), I wonder: why are they describing it like this? Is there any difference between these Swift strings and the way it is in Java? (Or C#, Python, Objective-C/NSString)
In a certain way, "mutable" and "immutable" only make sense when talking about reference types. If you try to extend it to value types, then all value types can be considered functionally equivalent to "immutable" reference types.
For example, consider a var
of type Int
. Is this mutable? Some of you might say, sure -- you can change its visible "value" by assigning (=
) to it. However, the same can be said of a var
of NSNumber
and NSString
-- you can change its visible value by assigning to it. But NSNumber
and NSString
are described as immutable classes.
What is really happening for reference types is that assigning to them causes the variable (a pointer) to point to a new object. Neither the old nor new object itself is "changed", but since it points to a different object, you "see" a new value.
What we mean when we say a class is "mutable" is that it offers an API (method or reference) to actually change the contents of the object. But how do we know that the object has changed? (rather it being a new object?) It's because we could have another reference to the same object, and changes to the object through one reference is visible through another reference. But these properties (pointing to different objects, having multiple pointers to the same object) inherently only apply to reference types. Value types, by definition, cannot have such "sharing" (unless part of the "value" is a reference type, like in Array
), and thus, the consequence of "mutability" cannot happen for value types.
So if you make an immutable class that wraps an integer, it would be operationally equivalent to an Int
-- in both cases, the only way to change a variable's value would be to assign (=
) to it. So Int
should also similarly be considered "immutable".
Value types in Swift are slightly more complex, because they can have methods, some of which can be mutating
. So if you can call a mutating
method on a value type, is it mutable? However, we can overcome this if we consider calling a mutating
method on a value type to be syntactic sugar for assigning a whole new value to it (whatever the method would mutate it to).
In Swift, Structures and Enumerations Are Value Types:
In fact, all of the basic types in Swift—integers, floating-point numbers, Booleans, strings, arrays and dictionaries—are value types, and are implemented as structures behind the scenes.
So a string is a value type that gets copied on assignment and cannot have multiple references, but its underlying character data is stored in a shareable, copy-on-write buffer. The API reference for the String
struct says:
Although strings in Swift have value semantics, strings use a copy-on-write strategy to store their data in a buffer. This buffer can then be shared by different copies of a string. A string’s data is only copied lazily, upon mutation, when more than one string instance is using the same buffer. Therefore, the first in any sequence of mutating operations may cost O(n) time and space.
So indeed, var
vs. let
declares a mutable vs. immutable binding to a character buffer that appears immutable.
var v1 = "Hi" // mutable var v2 = v1 // assign/pass by value, i.e. copies the String struct v1.append("!") // mutate v1; does not mutate v2 [v1, v2] // ["Hi!", "Hi"] let c1 = v1 // immutable var v3 = c1 // a mutable copy // c1.append("!") // compile error: "Cannot use mutating member on immutable value: 'c1' is a 'let' constant" v3 += "gh" // mutates v3, allocating a new character buffer if needed v3.append("?") // mutates v3, allocating a new character buffer if needed [c1, v3] // ["Hi", "High?"]
This is like non-final vs. final
Java String variables with two wrinkles.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With