Click here to Skip to main content
15,886,518 members
Articles / All Topics

When a Swift Immutable Collection Isn't Or Is At Least Immutable-ish

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
24 Feb 2015CPOL2 min read 4.5K  
When a Swift immutable collection isn't or is at least immutable-ish

Take the following code: struct Foo { var value: Int } let foo = [Foo(value: 1), Foo(value: 2)].

Take the following code:

C#
struct Foo
{
    var value: Int
}

let foo = [Foo(value: 1), Foo(value: 2)]

By using 'let foo', an immutable array has been created. As such, no members of this array can be replaced nor can an element's contents be modified. As such, both statements below result in compilation errors.

C#
foo[0] = Foo(value: 777) // error!
foo[0].value = 8 // error!

If foo were declared var, then both of the above statements would work.

This changes slightly when using reference types:

C#
class Bar
{
    var value: Int = 0

    init(value: Int)
    {
        self.value = value
    }
}

let bar = [Bar(value: 1), Bar(value: 2)]

bar[0] = Foo(value: 777) // error!
bar[0].value = 8 // allowed

The first case of trying to replace an element fails as before but modifying the instance referenced by that element is permitted. This is because the immutable collection holds a reference to the instance of Bar. Modifying the instance does not change the reference so the collection remains unchanged.

If you're making an effort to make your code as immutable (const) as possible (I'm from a C++ background so I am endeavour to making everything as const I can), then this is a gotcha to lookout for.

The only way to make Bar properly const is to provide a private setter for the member, assuming it's defined in its own source file (so this example doesn't work in a Plaground), i.e.,

C#
class Baz
{
    private(set) var value: Int = 0

    init(value: Int)
    {
        self.value = value
    }

    func f()
    {
        value = 88;
    }
}

Which now prevents value being assigned too.

C#
let baz = [Baz(value: 1), Baz(value: 2)]
baz[0].value = 8 // error!

Even if all the properties have private setters, there might be methods on the reference type that modify the instance, such as f() above. Just having an immutable collection of reference types is not sufficient to stop these mutating the instances referred to. Invoking f() in the above collection will change the value to 88, i.e.

C#
println("\(baz[0].value)") // Gives 1
baz[0].f()
println("\(baz[0].value)") // Gives 88

This differs to C++ where if a collection is const or is returned as a const reference (from a method) than not only is the container immutable but so are its contents. It would be good if Swift were to gain a mechanism that would mark the collection and its contents completely immutable other than having to use a value type.

Anyway, beware of making a collection of reference types immutable and assuming that both the collection and its contents cannot be modified!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader
United Kingdom United Kingdom
My day job is mostly working in C++ with a bit of C#. I write a fair amount of command line based tools and really wish they could have a GUI front-end to them hence why I spend my spare time working with WPF.

I started a blog few years back but didn't do a lot with it. I've started describing some of the interesting programming things I come across on it. Please take a look.

Comments and Discussions

 
-- There are no messages in this forum --