Rob
Rob

Reputation: 4224

Swift Protocol extension: cannot assign to property: '' is a get-only property

I think I don't understand how protocol extensions are supposed to work.

I tried this:

protocol P {
    var h: [String: Any] { set get }
}

extension P {
    var h: [String: Any] {
        get {
            return [:]
        }
        set {}
    }
}

struct S: P {   
    init() {
        self.h = ["o": "o"]
    }
}

My goal is that S has the properties of P and doesn't need to re-declare it in the struct definition.

However, when I create let s = S(), s.h always equals [:] and not ["o":"o"].

Of course, this is because my setter is empty, but I don't know how to do what I want to achieve here.

Thanks for your help.

Upvotes: 4

Views: 3863

Answers (2)

Wilson Desimini
Wilson Desimini

Reputation: 770

You were close, you just have to restate the h variable in the S Struct

protocol P {
    var h: [String: Any] { set get }
}

extension P {
    var h: [String: Any] {
        get {
            return [:]
        }
        set {}
    }
}

struct S: P {
    var h: [String: Any]

    init() {
        self.h = ["o": "o"]
    }
}

let s = S()
print(s.h) // ["o": "o"]

Upvotes: 0

Rob Napier
Rob Napier

Reputation: 299275

My goal is that S has the properties of P and doesn't need to re-declare it in the struct definition.

That's not possible in the way you're trying to do this. Protocols require certain behaviors. Conforming types must provide those behaviors. If you require storage to implement the behavior, than the conforming type has to provide the storage.

It's fine if you don't want storage, then the extension can just return values like yours does. But you can't return computed values and also have storage. What you're trying to do isn't possible. You're thinking of classes, not protocols. (If you want classes, that's fine. Use classes. There's nothing wrong with them.)

A good way to think about why this isn't possible in Swift is to consider the following case. Assume your P protocol exists in some form that achieves what you want. Now in one module we define:

struct S {}

This struct requires no storage. Any calls to S() in that module allocate nothing. (Alternately, give S some properties, and it'll allocate some specific amount of memory.)

Now in some other module, extend S to conform to P:

extension S: P {}

Where would the storage for h go? Instances may already exist at the point that this extension is even loaded. What if there were multiple protocols that wanted additional storage and all were attached to S. What offsets into the S struct should those properties be? The order would be very important to compiled code. This isn't impossible to solve (ObjC does it using a technique called associated objects), but it's a large addition to the language, and prevents a number of important optimizations, and Swift doesn't do things that way.

Upvotes: 3

Related Questions