In Swift, what are the semantics of subscripts yielding (or receiving) non-object types? - swift

The Swift book does not clearly document the semantics of non-object types in conjunction with subscripts. The most obvious example is a Dictionary whose value type is an Array: Array is a struct. Therefore, when a subscript returns it, we should expect it to be copied. This, however, is very inconvenient. Fortunately, it seems also not to be the case—at least not in Xcode 8.2.1 (Swift 3.1?).
Consider these examples:
var a: [Int] = [0] // [0]
var b = a // [0]
b.append(1)
b // [0, 1]
a // [0]
As we expect, the array a is copied when it is assigned to b. In contrast,
var h: [Int: [Int]] = [0: [0]] // [0: [0]]
h[0]!.append(1)
h[0] // [0, 1]
If the subscript were simply returning the value using ordinary semantics, we would expect that h[0] would still equal [0] after h[0]!.append(1), but in fact it is the array in the dictionary that is appended to. We can even see if we like that the value remains at the same location in memory, suggesting that it is semantically the same array that was appended to. (Being at the same location does not imply that, but it is not in conflict with that, either.)
var h: [Int: [Int]] = [0: [0]] // [0: [0]]
var aptr: UnsafePointer<[Int]>? = nil
withUnsafePointer(to: &h[0]!) { aptr = $0 }
aptr // UnsafePointer(0x7FFF5351C2C0)
h[0]!.append(1)
withUnsafePointer(to: &h[0]!) { aptr = $0 }
aptr // UnsafePointer(0x7FFF5351C2C0)
This fortunate but seemingly-undocumented behavior does not apply only to Arrays as Dictionary values.
struct S: CustomStringConvertible {
var i: Int = 0
var description: String { return "S(i=\(self.i))" }
}
var g: [Int: S] = [0: S()]
g[0]! // S(i=0)
g[0]!.i = 5
g[0]! // S(i=5)
And, in fact, it does not even apply only to the subscripts of Dictionary types.
struct T {
var s: S? = S()
subscript(x: Int) -> S? {
get {
return self.s
}
set(s) {
self.s = s
}
}
}
var t = T()
t[0]! // S(i=0)
var tptr: UnsafePointer<T>? = nil
withUnsafePointer(to: &t) { tptr = $0 }
tptr // UnsafePointer(0x1007F6DB8)
t[0]!.i = 5
t[0]! // S(i=5)
withUnsafePointer(to: &t) { tptr = $0 }
tptr // UnsafePointer(0x1007F6DB8)
Notably, this does somehow involve the setter: If the set under subscript is removed from the definition of T, the statement t[0]!.i = 5 produces the error
error: cannot assign to property: subscript is get-only
My preferred answer would be a pointer to some Swift documentation that clearly explains the semantics of modifications to non-object values obtained through subscripts. It appears that the language behaves as I would like it to, but I'm not comfortable relying on this behavior while it seems inconsistent with the documentation.

Array is implemented using copy-on-write, so it is not in fact copied each time it is assigned but only when it needs to be as determined by its internal state. It seems this behaviour is implemented in such a way that it is not triggered after being returned from a subscript.
update
Array subscripts are implemented using addressors, essentially the subscript accesses the array element directly. You can read some details in this document: https://github.com/apple/swift/blob/master/docs/proposals/Accessors.rst
Particularly note the section Mixed addressors, quote: 'Mixed addressors have now been adopted by Array to great success', perhaps dictionaries are using the same system now.
Also see this tweet: https://mobile.twitter.com/slava_pestov/status/778488750514970624
by someone who apparently works on the swift compiler at Apple, the related thread has some interesting links.
But essentially the answer to your question is that this isn't documented in a user friendly way, it is using special optimisations behind the scenes to make sure it works efficiently. I agree that a detailed explanation in the documentation would be helpful!
If you are concerned at using undocumented behaviour in your code a workaround would be to wrap your value type in a class before storing it in an array or dictionary. But it seems unlikely that the Swift team will make a change that breaks existing code in this way.

Related

Is it possible to make array reference immutable, but array content mutable?

In Java, we can make an array reference immutable, and array content mutable, by using final keyword
Java
final int[] array = {1, 2, 3};
// Ok. Array content mutable.
array[0] = 9;
// Compiler error. Array reference immutable.
array = new int[]{4, 5, 6};
In Swift, they take one step further. Using let keyword, will make both array reference, and array content immutable.
Swift
let array = [1, 2, 3]
// Compiler error. Array content immutable.
array[0] = 9
// Compiler error. Array reference immutable.
array = [4, 5, 6]
In Swift, is it possible to make array reference immutable, but array content mutable?
The answer for your question is "yes" and "no", depends on what you have.
If you decide to declare a simple "let" constant, you can't modify it.
Why ? Because it prevents you to side effects (and you have some optimization).
For example if you just want to browse a list and print values, you don't modify the list.
myArray = [1,2,3]
for element in myArray {
print(element)
}
Why it can be cool ? Now if you know that you don't want to modify your list, it prevents you to use functions that can modify your list. It will save your time and avoid some behavior that you don't expect.
If you declare a var and you don't modify the value, Swift will tell you too.
Moreover, the concept of immutable in Swift is interesting if you use a struct or a class.
Imagine you have this structure and this class:
struct TestStruct {
var myInt: Int
init(myInt: Int) {
self.myInt = myInt
}
}
struct TestClass {
var myInt: Int
init(myInt: Int) {
self.myInt = myInt
}
}
In this structure you have myIntwhich is a var. What happens if you try to declare a TestStructure and a TestClass object with a let constant ?
let testStruct = Test(myInt: 3)
// Cannot assign to property: 'test' is a 'let' constant
test.myInt = 5
let testClass = Test(myInt: 3)
// It works
test.myInt = 5
In a struct, the let is propagated for every field, which is not the case for a class.
Using let keyword, will make both array reference, and array content immutable.
This isn't correct. There is no "array reference" here. An array is a value, just like an integer is a value. There is no "array reference." Variables can be let or var, but that doesn't change the nature of their value. You wouldn't say that var n = 4 made "4" mutable. Similarly, var ns = [1,2,3] doesn't make [1,2,3] mutable. It just means you can change what ns refers to. Calling ns.append(5) is just like n += 1. In each case they assign a new value. They don't mutate the old value.
As an implementation and optimization detail, it is possible that the underlying array storage that was used for ns will be mutated and used for the new ns value. But this is invisible to the caller. For example:
var array = [1,2] {
didSet { print("\(oldValue) -> \(array)") }
}
array.append(1)
array = [1,2,1]
// [1, 2] -> [1, 2, 1]
// [1, 2, 1] -> [1, 2, 1]
There's no deep difference between the append and the assignment. They are both assignments. And notice that setting the value to the same value is still just an assignment.
I'm harping on this because you can't just translate over a Java approach and have it work if your Java code relies on shared mutable state (where one part of the program modifies an array and others are supposed to have their reference update). But if your Java works that way, I recommend improving your Java to reduce its reliance on that. As long as you generally just pass values and return values, then it'll work exactly the same in Swift as in Java.
If you still need this kind of mutable array, then you can build one fairly easily by wrapping an Array in a class:
final class ArrayRef<Element>: MutableCollection, ExpressibleByArrayLiteral {
private var elements: [Element] = []
init(arrayLiteral elements: Element...) {
self.elements = elements
}
var startIndex: Int { elements.startIndex }
var endIndex: Int { elements.endIndex }
func index(after i: Int) -> Int { elements.index(after: i) }
subscript(position: Int) -> Element {
get { elements[position] }
set { elements[position] = newValue }
}
}
let array: ArrayRef = [1, 2, 3]
// Ok. "Array" content mutable.
array[0] = 9
// Compiler error. "Array" is immutable.
array = [4, 5, 6]
(This is a very simple and unoptimized implementation. With more work you can make it more efficient and improve the interface.)
But I don't particularly recommend this unless you really need it. There's a reason it doesn't exist in stdlib.

Crash in Swift struct definition. Have workaround but would like to improve understanding

I had the follow struct definition that would get a run-time crash in Release builds but not Debug builds. An uninitialised variable or perhaps a difference in initialise order?
struct Location:Codable {
struct Commute {
static let Times = [
["seconds" : 30],
["seconds" : 45],
["seconds" : 60],
["seconds" : 90]
]
static let count = Commute.Times.count
/*
next code line crashes on initialisation
XCode: specialized _ArrayBuffer._checkInoutAndNativeTypeCheckedBounds(_:wasNativeTypeChecked:) [inlined]
*/
static let Default = Commute.Times[Commute.Times.endIndex]
...
}
}
This looks like my use of Commute.Times.endIndex is used a little before Commute.Times is initialized so that endIndex is referenceless.
I can workaround this by replacing the 'offending' line with:
static let Default = Commute.Times[Commute.count - 1]
Have I used static correctly and defined the struct correctly? It's my understanding in this area I want to clarify.
A bonus:
Is there a cleaner way to set Default to the last item of Times than specifying count - 1?
This code should crash in either debugging or release mode.
endIndex definition-
The array’s “past the end” position—that is, the position one greater
than the last valid subscript argument.
You should get an "Index out of range" error while debugging
Array has a last member, which is optional (because you can have an empty array). If you know it is in fact non-empty you can force unwrap it:
static var Default: [String: Int] = { Commute.Times.last! }()
Although honestly I feel like its weird that you have an array of one element dictionaries. Perhaps NSOrderedSet or an array of tuples [(String, Int)] is more appropriate here depending on your actual use case.

A Constant Array's Items Can(Cannot) Be Modified If They Are Reference(Value) Types. Why?

Please have a look at the following code and note the compiler error on the last line:
class C {
var value: Int
init( _ value: Int) { self.value = value }
}
let array1 = [C(1), C(2), C(3)]
array1[0].value = 4
struct S {
var value: Int
}
let array2 = [S(value: 1), S(value: 2), S(value: 3)]
array2[0].value = 4 // Error: Cannot assign to property: 'array2' is a 'let' constant
From the compiler error I want to conclude that the item at index 0 is being read from array2, modified, and then written back to array2. What else could produce the result that there is an attempt to modify array2? But, if my reasoning is correct, then why does the same thing not happen with array1?
Classes Are Reference Types
Unlike value types(struct), reference types are not copied when they are assigned to a variable or constant, or when they are passed to a function. Rather than a copy, a reference to the same existing instance is used instead.
Please refer Apple documentation about Class & struct

Optional vs Bound value assigning var from array

I want to check if there is a value in a array and if so assign to a String using a if-left statement:
if let scoreValue = scoreValueArray[element!]{
// do something with scoreValue
}
Error: Bound value in a conditional binding must be of optional type
So tried changing the ! to ? but error persists.
Any input appreciated.
scoreValueArray is an array of strings, where a String value is appended to array if a condition is met, then array is saved to NSUserdefaults.
So element is a int which corresponds to a index in the array, bt only if the index is occupied with a String, so
scoreValueArray[element!]
could return an 'Index out of bounds', hence want to use the if-let.
Although the accepted answer clearly puts why optional binding is not available in the current implementation, it doesn't provide with a solution.
As it is shown in this answer, protocols provide an elegant way of safely checking the bounds of an array. Here's the Swift 2.0 version:
extension Array {
subscript (safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}
Which you can use like this:
let fruits = ["Apple", "Banana", "Cherry"]
if let fruit = fruits[safe: 4] {
// Do something with the fruit
}
It's not clear what type your scoreValueArray is, but for the sake of this answer, I'm going to assume it's an array of Int.
var scoreValueArray: Array<Int>
Now, if we look the definition of the Array struct, we'll find this:
struct Array<T> : MutableCollectionType, Sliceable {
// other stuff...
subscript (index: Int) -> T
// more stuff
}
So, calling the subscript method on our array (which is what we do when we say scoreValueArray) returns a non-optional. And non-optionals cannot be used in the conditional binding if let/if var statements.
We can duplicate this error message in a more simple example:
let foo: Int = 3
if let bar = foo {
// same error
}
This produces the same error. If we instead do something more like the following, we can avoid the error:
let foo: Int? = 3
if let bar = foo {
// perfectly valid
}
This is different from a dictionary, whose subscript method does return an optional (T?). A dictionary will return a value if the key passed in the subscript is found or nil if there is no value for the passed key.
We must avoid array-index-out-of-bounds exceptions in the same way we always do... by checking the array's length:
if element < scoreValueArray.count {
scoreValue = scoreValueArray[element]
}

How to create a type that either hold an `Array<Int>` or `UnsafePointer<UInt8>`

I'm doing some performance testing of Swift vs Objective-C.
I created a Mac OS hybrid Swift/Objective-C project that creates large arrays of prime numbers using either Swift or Objective-C.
It's got a decent UI and shows the results in a clear display. You can check out the project on Github if you're interested. It's called SwiftPerformanceBenchmark.
The Objective-C code uses a malloc'ed C array of ints, and the Swift code uses an Array object.
The Objective C code is therefore a lot faster.
I've read about creating an Array-like wrapper around a buffer of bytes using code like this:
let size = 10000
var ptr = UnsafePointer<Int>malloc(size)
var bytes = UnsafeBufferPointer<Int>(start: ptr, count: data.length)
I'd like to modify my sample program so I can switch between my Array<Int> storage and using an UnsafeBufferPointer<Int> at runtime with a checkbox in the UI.
Thus I need a base type for my primes array that will hold either an Array<Int> or an UnsafeBufferPointer<Int>. I'm still too weak on Swift syntax to figure out how to do this.
For my Array- based code, I'll have to use array.append(value), and for the UnsafeBufferPointer<Int>, which is pre-filled with data, I'll use array[index]. I guess if I have to I could pre-populate my Array object with placeholder values so I could use array[index] syntax in both cases.
Can somebody give me a base type that can hold either an Array<Int> or an UnsafeBufferPointer<Int>, and the type-casts to allocate either type at runtime?
EDIT:
Say, for example, I have the following:
let count = 1000
var swiftArray:[Int]?
let useSwiftArrays = checkbox.isChecked
typealias someType = //A type that lets me use either unsafeArray or swiftArray
var primesArray: someType?
if useSwiftArrays
{
//Create a swift array version
swiftArray [Int](count: count, repeatedValue: 0)
primesArray = someType(swiftArray)
}
else
{
var ptr = UnsafePointer<Int>malloc(count*sizeof(Int))
var unsafeArray = UnsafeBufferPointer<Int>(start: ptr, count: data.length)
primesArray = someType(unsafeArray)
}
if let requiredPrimes = primesArray
{
requiredPrimes[0] = 2
}
#MartinR's suggestion should help get code that can switch between the two. But there's a shortcut you can take to prove whether the performance difference is between Swift arrays and C arrays, and that's to switch the Swift compiler optimization to -Ounchecked. Doing this eliminates the bounds checks on array indices etc that you would be doing manually by using unsafe pointers.
If I download your project from github and do that, I find that the Objective-C version is twice as fast as the Swift version. But... that’s because sizeof(int) is 4, but sizeof(Int) is 8. If you switch the C version to use 8-byte arithmetic as well...
p.s. it works the other way around as well, if I switch the Swift code to use UInt32, it runs at 2x the speed.
OK, it’s not pretty but here is a generic function that will work on any kind of collection, which means you can pass in either an Array, or an UnsafeMutableBufferPointer, which means you can use it on a malloc’d memory range, or using the array’s .withUnsafeMutableBufferPointer.
Unfortunately, some of the necessities of the generic version make it slightly less efficient than the non-generic version when used on an array. But it does show quite a nice performance boost over arrays in -O when used with a buffer:
func storePrimes<C: MutableCollectionType where C.Generator.Element: IntegerType>(inout store: C) {
if isEmpty(store) { return }
var candidate: C.Generator.Element = 3
var primeCount = store.startIndex
store[primeCount++] = 2
var isPrime: Bool
while primeCount != store.endIndex {
isPrime = true
var oldPrimeCount = store.startIndex
for oldPrime in store {
if oldPrimeCount++ == primeCount { break }
if candidate % oldPrime == 0 { isPrime = false; break }
if candidate < oldPrime &* oldPrime { isPrime = true; break }
}
if isPrime { store[primeCount++] = candidate }
candidate = candidate.advancedBy(2)
}
}
let totalCount = 2_000_000
var primes = Array<CInt>(count: totalCount, repeatedValue: 0)
let startTime = CFAbsoluteTimeGetCurrent()
storePrimes(&primes)
// or…
primes.withUnsafeMutableBufferPointer { (inout buffer: UnsafeMutableBufferPointer<CInt>) -> Void in
storePrimes(&buffer)
}
let now = CFAbsoluteTimeGetCurrent()
let totalTime = now - startTime
println("Total time: \(totalTime), per second: \(Double(totalCount)/totalTime)")
I am not 100% sure if I understand your problem correctly, but perhaps
this goes into the direction that you need.
Both Array and UnsafeMutablePointer conform to MutableCollectionType (which requires a subscript getter and setter).
So this function would accept both types:
func foo<T : MutableCollectionType where T.Generator.Element == Int, T.Index == Int>(inout storage : T) {
storage[0] = 1
storage[1] = 2
}
Example with buffer pointer:
let size = 2
var ptr = UnsafeMutablePointer<Int>(malloc(UInt(size * sizeof(Int))))
var buffer = UnsafeMutableBufferPointer<Int>(start: ptr, count: size)
foo(&buffer)
for elem in buffer {
println(elem)
}
Example with array:
var array = [Int](count: 2, repeatedValue: 0)
foo(&array)
for elem in array {
println(elem)
}
For non-mutating functions you can use CollectionType
instead of MutableCollectionType.