Mutable type eraser with struct like copy semantics

I built a protocol with associated types, now I wish to make a type eraser for it, but since type erasers always use reference types somehow I can't find any strategy to have a type eraser that would let assign some variable x to y and mutating y without mutating x :

var x = AnyFoo(something) // Where something is a struct conforming to protocol Foo
var y = x
x.bar = 10
y.bar = 15 // Without modifying the value for x.foo

I tried different approaches, one inspired from AnySequence and the other inspired from this tutorial but no matter how I try to implement it I always end up being blocked when trying to avoid x and y to share a reference to the same underlying objects.

What is the right approach to do that ?

Is this what you had in mind?

/// Protocol to allow cloning.
protocol Clonable {
    var clone: Self { get }
}

/// The protocol which implementations of which will have their type erased to `AnyBox`.
protocol Box {
    associatedtype BoxType: Clonable
    var boxed: BoxType { get set }
}

/// Type erased instance of a `Box`.
struct AnyBox<T>: Box where T: Clonable {
    typealias BoxType = T
    private var boxedClone: T
    init<B>(_ b: B) where B: Box, B.BoxType == T {
        boxedClone = b.boxed.clone // Clone to allow mutation.
    }
    var boxed: T {
        get { return boxedClone }
        set { boxedClone = newValue }
    }
}

/// Make Ints clonable so that they can be boxed.
extension Int: Clonable {
    var clone: Int { return self }
}

/// A box to type erase.
struct IntBox: Box {
    typealias BoxType = Int
    var boxed: Int
}

let i = IntBox(boxed: 0)
var x = AnyBox(i) // Type erase.
let y = x
x.boxed = 1
x.boxed // 1
y.boxed // 0

Thank you! Would this be able to erase anything else ? If I'm right the type T of boxedClone isn't guaranteed to conform to Box (actually the initialiser enforces it but compiler won't know it outside of the init body).

I think in hindsight my solution is not what you want. Perhaps this is nearer to what you need:

/// The protocol which implementations of which will have their type erased to `AnyBox`.
protocol Box {
    associatedtype BoxedType
    var boxed: BoxedType { get set }
}

/// A box of a value type (struct, enum) to type erase.
struct ValueBox<T>: Box {
    typealias BoxType = T
    var boxed: T // Int is used as an example, but could be any struct or enum.
}

/// Example mutable reference type (class).
class Reference<T> {
    var value: T
    init(_ initial: T) {
        value = initial
    }
}

/// A box of a reference type (class) to type erase that uses copy-on-write to maintain sperate copies (like Array does).
struct ReferenceBox<T>: Box {
    typealias BoxType = T // Note Int not Reference, since this box takes care of the copying.
    var boxed: Reference<T> // Reference is used as an example, but clould be any class.
    init(boxed: Reference<T>) { self.boxed = boxed }
}

/// Type erased version of a *known* set of `Box` types.
enum AnyKnownBox<T>: Box {
    case valueBox(ValueBox<T>)
    case referenceBox(ReferenceBox<T>)
    
    typealias BoxedType = T
    var boxed: T {
        get {
            switch self {
            case .valueBox(let b):
                return b.boxed
            case .referenceBox(let b):
                return b.boxed.value
            }
        }
        set {
            switch self {
            case .valueBox(_):
                self = .valueBox(ValueBox(boxed: newValue))
            case .referenceBox(_):
                self = .referenceBox(ReferenceBox(boxed: Reference(newValue)))
            }
        }
    }
}

var v = ValueBox(boxed: 0)
var x = AnyKnownBox.valueBox(v) // Type erase away ValueBox.
let y = x
v.boxed = 2
x.boxed = 1
v.boxed // 2
x.boxed // 1
y.boxed // 0

var r = Reference(0)
var rb = ReferenceBox(boxed: r)
var rx = AnyKnownBox.referenceBox(rb) // Type erase away ReferenceBox.
let ry = rx
r.value = 3
rb.boxed = Reference(2)
rx.boxed = 1
r.value // 3
rb.boxed.value // 2
rx.boxed // 1
ry.boxed // 3 - ry still ponts to r wheras both rb and rx have new references created when they were mutated.

Another approach is to just represent your types in an enum directly rather than have a protocol and type erase the protocol.