How to programmatically detect if copy on write occurs

For this specific case you can use a couple techniques to get rid of CoW.
If you make the type not a class but a struct you can consume it which should get rid of the intermediate copy:

struct C {
    enum State {
        case none
        case some([Int])
    }
    var state = State.some([])

    mutating func append(_ i: Int) {
        switch (consume self).state {
        case .none:
            self = .init(state: .some([i]))
        case .some(var value):
            value.append(i)
            self = .init(state: .some(value))
        }
    }
}

Copyable structs don't support partial consumption so you will need to consume self fully. A ~Copyable struct at least supports partial consumption but sadly doesn't yet support reinitializing fields after they are consumed.
It still improves it slightly:

struct C2: ~Copyable {
    enum State: ~Copyable {
        case none
        case some([Int])
    }
    var state = State.some([])

    mutating func append(_ i: Int) {
        switch consume state {
        case .none:
            self = .init(state: .some([i]))
        case .some(var value):
            value.append(i)
            self = .init(state: .some(value))
        }
    }
}

The real advantage of making everything ~Copyable is that we can now use UniqueArray (from swift-collections today or eventually in the standard library):

struct C3: ~Copyable {
    enum State: ~Copyable {
        case none
        case some(UniqueArray<Int>)
    }
    var state = State.some(.init())

    mutating func append(_ i: Int) {
        switch consume state {
        case .none:
            var array = UniqueArray<Int>()
            array.append(i)
            self = .init(state: .some(array))
        case .some(var value):
            value.append(i)
            self = .init(state: .some(value))
        }
    }
}

which actually grantees that we never trigger CoW as it doesn't even support it to begin with.

The consume of state and the reinitializing of the enum is still just a workaround for a missing inout for pattern matching feature.

6 Likes