[swift-evolution] References in Value Types (Deep-Copy-OnWrite)

Andrew Bennett cacoyi at gmail.com
Thu Dec 17 07:51:26 CST 2015


This isn't a proposal, more something to get a discussion started.

TL;DR: You can make a struct look like a class, a class look like a struct,
a mutable type appear immutable.

I think that Swift is doing these things as it should. However I think that
there is room for mistakes, and it would be nice to be aware of those
mistakes and guard against them.

Consider the following code:

private class MyClass {

    var value: Int

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

}

struct MyValue {

    private let reference: MyClass

    init(value: Int) {

        self.reference = MyClass(value: value)

    }

    var value: Int {

        get { return self.reference.value }

        set { self.reference.value = newValue }

    }

}


var myVariable = MyValue(value: 123)

print(myVariable.value) // "123"


let myOtherVariable = myVariable


myVariable.value = 456

print(myOtherVariable.value) // "456"!


Are all of our assumptions correct?
 * Is myVariable's a value type? probably
 * Is myVariable copy-on-write? perhaps
 * Should we be able to write to myVariable.value despite reference being
let? probably
 * Is this code unsafe, or misleading? yes. Can we prevent that? maybe

Some other code, this is not perfect but it mostly makes MyValue
copy-on-write:

protocol DeepCopier {

    typealias ObjectType: AnyObject

    static func deepCopy(that: ObjectType) -> ObjectType

}

struct UniqueReference<Copier: DeepCopier> {

    private var _reference: Copier.ObjectType

    init(var reference: Copier.ObjectType) {

        if isUniquelyReferencedNonObjC(&reference) {

            _reference = reference

        }

        else {

            _reference = Copier.deepCopy(reference)

        }

    }

    var reference: Copier.ObjectType { return self._reference }

    mutating func update(block: Copier.ObjectType -> Void) {

        if !isUniquelyReferencedNonObjC(&self._reference) {

            self._reference = Copier.deepCopy(self._reference)

        }

        block(self._reference)

    }

}


private class MyClass: NonObjectiveCBase {

    var value: Int

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

    func deepCopy() -> MyClass {

        return MyClass(value: self.value)

    }

    private struct Copier: DeepCopier {

        static func deepCopy(that: MyClass) -> MyClass {

            return MyClass(value: that.value)

        }

    }

}


struct MyValue {

    private var myReference: UniqueReference<MyClass.Copier>

    init(value: Int) {

        self.myReference = UniqueReference(reference: MyClass(value: value))

    }

    var value: Int {

        get { return self.myReference.reference.value }

        set { self.myReference.update { $0.value = newValue } }

    }

}


var myVariable = MyValue(value: 123)

print(myVariable.value) // "123"


let myOtherVariable = myVariable


myVariable.value = 456

print(myOtherVariable.value) // "123"

print(myVariable.value) // "456"


Even more code, I don't trust anything any more:

struct MyValue {

    init(value: Int) { }

    var value: Int {

        get { return Int(arc4random()) }

        set { }

    }

}


The public interface for MyValue in all these cases looks totally innocent:

struct MyValue {

    init(value: Int)

    var value: Int

}


Protocols are the same, they provide no guarantees about immutability, or
deep-copy-on-write:

protocol SomeValue {

    init(value: Int)

    var value: Int { get }

}


The results may be a bit surprising.

 * Perhaps we could introduce a "const" keyword like in other languages, so
the class cannot be mutated (how do we even scope this?).
 * Perhaps we could prevent structures from holding references (no classes,
non-pure blocks, no arc4random, no AnySequence, ...).
 * Perhaps we can add a @copy_on_write keyword on structures that enforces
certain guarantees? (how?)
 * Perhaps add language support for DeepCopier (with a better name,
probably a keyword rather than a protocol), that allows anything to be
copied like a structure (my preference).
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151218/ff18025c/attachment.html>


More information about the swift-evolution mailing list