In The Swift Programming Language, in the section on Strings, subsection String Mutability, it says this:
You indicate whether a par
To answer the original question. Strings in Swift are not the same as Strings in Java or C# (I don't know about Python). They differ in two ways.
1) Strings in Java and C# are reference types. Strings in Swift (and C++) are value types.
2) Strings in Java and C# are always immutable even if the reference is not declared final or readonly. Strings in Swift (and C++) can be mutable or immutable depending on how they are declared (let vs var in Swift).
When you declare final String str = "foo"
in Java you are declaring an immutable reference to an immutable String object. String str = "foo"
declares a mutable reference to an immutable String object.
When you declare let str = "foo"
in Swift you are declaring an immutable string. var str = "foo"
declares a mutable string.
First, you're using immutable method that makes a new value instance. You need to use mutating
methods such as extend
to perform the operation in mutating semantic.
What you did at here is creating a new immutable string and binding it into an existing name.
var variableString = "Horse"
variableString += " and carriage"
This is mutating the string in-place without any extra name binding.
var variableString = "Horse"
variableString.extend(" and carriage")
Second, purpose of immutable/mutable separation is providing easier and safer programming model. You can make more assumptions safely on immutable data structure, and it can eliminate many headache cases. And this helps optimisation. Without immutable type, you need to copy whole data when you passing it into a function. Otherwise, original data can be mutated by the function, and this kind of effect is unpredictable. Then such functions need to be annotated like "This function does not modify passed-in data.". With immutable type, you can safety assume the function cannot modify the data, then you don't have to copy it. Swift does this implicitly and automatically by default.
Yes, actually mutable/immutable difference is nothing more than difference in interface in higher level languages like Swift. Value semantic simply means it does not support identity comparison. As many details are abstracted out, internal implementation can be anything. Swift code comment is clarifying the string is using COW tricks, and then I believe both of the immutable/mutable interfaces are actually mapped to mostly-immutable implementations. I believe you will get pretty same result regardless of interface choice. But still, this provides benefits of immutable data type I mentioned.
Then, the code example, actually does same thing. The only difference is you cannot mutate the original that bound to its name in immutable mode.
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.
Swift strings are values, not objects. When you change a value, it becomes a different value. So in the first case, using var
, you are simply assigning the new value to the same variable. Whereas let
is guaranteed not to have any other value after you assign it, so it gives a compiler error.
So to answer your question, Swift strings receive pretty much the same treatment as in Java, but are considered as values rather than objects.
Actually, Swift's Strings appear to be just like Objective-C (immutable) NSString
; I found this in the documentation you linked to -
Swift’s String type is bridged seamlessly to Foundation’s NSString class. If you are working with the Foundation framework in Cocoa or Cocoa Touch, the entire NSString API is available to call on any String value you create, in addition to the String features described in this chapter. You can also use a String value with any API that requires an NSString instance.
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).