85
votes

What is a slice in Swift and how does it differ from an array?

From the documentation, the type signature of subscript(Range) is:

subscript(Range<Int>) -> Slice<T>

Why not return another Array<T> rather than a Slice<T>?

It looks like I can concatenate a slice with an array:

var list = ["hello", "world"]
var slice: Array<String> = [] + list[0..list.count]

But this yields the error:

could not find an overload for 'subscript' that accepts the supplied arguments

var list = ["hello", "world"]
var slice: Array<String> = list[0..list.count]

What is a slice?

3

3 Answers

97
votes

The slice points into the array. No point making another array when the array already exists and the slice can just describe the desired part of it.

The addition causes implicit coercion, so it works. To make your assignment work, you would need to coerce:

var list = ["hello", "world"]
var slice: Array<String> = Array(list[0..<list.count])
22
votes

Note: This answer is happily invalid as of Swift beta 3, since arrays are now true value types.


@matt is correct, above - the Slice<T> points into the array. That seems contrary to the way Swift handles all the other data types we're working with, since it means the value of the slice can change even if it's declared as a constant:

var arr = ["hello", "world", "goodbye"]    // ["hello", "world", "goodbye"]
let slice = arr[0..2]                      // ["hello", "world"]
arr[0] = "bonjour"
println(slice)                             // ["bonjour", "world"]

The worst part is that the slice acts just like an array. Given that in Swift we have an expectation of immutability it seems dangerous that the subscripted values of the slice can change without warning:

println(slice[1])                          // "world"
arr[1] = "le monde"
println(slice[1])                          // "le monde"

But if the underlying array changes too drastically, they get unhooked:

arr.removeAtIndex(0)                       // this detaches slice from arr
println(slice)                             // ["bonjour", "le monde"]
arr[0] = "hola"
println(slice)                             // ["bonjour", "le monde"]
14
votes

Summary:

The answers above were true up until Beta 3 (and may change again in future releases)

Slice now acts just like an array, but as @matt said above, is effectively a shallow copy to an array under the hood, until a change is made. Slices (now) see a snapshot of the original values,

Also note that slice syntax has changed:

[from..upToButNotIncluding] -> [from..<upToButNotIncluding]

Example:

var arr = ["hello", "world", "goodbye"] // ["hello", "world", "goodbye"]
var arrCopy = arr
let slice = arr[0..<2]                  // ["hello", "world"]
arr[0] = "bonjour"
arr                                     // ["bonjour", "world", "goodbye"]
arrCopy                                 // ["hello", "world", "goodbye"]
slice                                   // ["hello", "world"]

This allows much more uniform processing, as it is simpler (IMHO) to do python style list processing - filtering one list to make another. per Matt's answer prior to Beta 3, you had to create a temporary array in order to map a slice. The new code is now simpler:

class NameNumber {
    var name:String = ""
    var number:Int = 0

    init (name:String, number:Int) {
        self.name = name
        self.number = number
    }
}

var number = 1
let names = ["Alan", "Bob", "Cory", "David"]
let foo = names[0..<2].map { n in NameNumber(name:n, number:number++) }
foo     // [{name "Alan" number 1}, {name "Bob" number 2}]

(though to be fair, foo is still a slice)

Reference:

http://adcdownload.apple.com//Developer_Tools/xcode_6_beta_3_lpw27r/xcode_6_beta_3_release_notes__.pdf

Important Changes, Issues Resolved, - Swift Language, Paragraph 1

"Array in Swift has been completely redesigned to have full value semantics like Dictionary and String...m"