I can't recreate my class using protocols - extension stored properties

I'm trying to use structs rather than classes, however I don't see an obvious way to replace inheritance in a reasonable way.

I have this class:

/// Base class.
class Entity {
    private(set) var position = Vector2D<Double>(x: 0, y: 0)

    /// Used to set a new position for the object.
    /// - Parameters:
    ///   - x: Horizontal position.
    ///   - y: Vertical position.
    final func setPosition(x: Double, y: Double) {
        self.position.x = x
        self.position.y = y
    }

    /// Entity specific logic, called every frame.
    func action() {}
}

I can definitely replace overriding action() with a protocol, which is really nice :slight_smile:
I can also have setPosition() in a protocol extension, however this is as far as I can go.
All I can do with position seems to be adding it to the protocol, forcing me to implement it manually for every single entity, which is not fun. It also seems to take away the ability to have encapsulation like I get from private(set)

Why is it not allowed to declare private(set) var position = Vector2D<Double>(x: 0, y: 0) in my extension? I don't want to have to implement the same basic functionality for every entity type, but I also don't want to use classes just for this one feature.

The short answer is "protocols cannot define stored properties". This falls out fairly naturally from the fact that protocol conformances can be defined outside the module that defines the type: how could such a protocol add storage?

The best you can do is have the protocol require that your type have the storage. Your type definition will still have to actually define that storage.

1 Like

You're in the unfortunate area where you want to express a common "look" of types, i.e. you want to express them having a specific property in common. Unlike inheriting this, however, protocols conceptually leave the responsibility of how they achieve this look (i.e. their public interface) completely up to the types adopting them.

While having a meant to provide a default would surely be nice this is actually more complicated than it may appear on first glance (@lukasa mentioned one of the problems, I guess there's more). I think there were some pitches about offering developers a means to provide custom synthesis for their protocols (like the compiler does for stuff like Equatable, etc., see here and here), but I don't know how far that has come.

Generally speaking I want to politely challenge the frame of your question a bit, @TeamPuzel:
Is it really the best approach to go over various method and property declarations in superclasses and simply try to extract them into protocols? The example seems reasonable, but perhaps by doing it this way you're blind-siding yourself from a higher level approach, i.e. a different concept. I'm not saying that's the case, but as I had some difficulties changing some old ways of mine I want to emphasize that a bit.
Other than that: Is it really that terrible if each struct has to (re)define those properties or is it perhaps even an advantage in some cases (like more complex types actually storing them in a different way and just offering calculated properties to satisfy the protocol?).
I know it's redundant, but it also might be more readable for new contributors (for a struct you just have to look at one place to see its members and don't care about inherited ones).
What you posted shows coordinates, which are admittedly repetitive over different geometrical objects and such, but it's just one line here, no?

2 Likes

I agree with the way protocols are designed, I think when written by someone else, structs are a lot more understandable than classes with layers of inheritance.
I'm looking at this from a perspective where I'm only using a class to make writing code easier for myself, paying for the convenience with reference counting and always storing everything on the heap.

I'm working on this by myself so struct properties being obvious doesn't really matter to me in this case.
I wouldn't mind if the choice between struct and class was just the way they work, but classes are slower so I don't want them in my code doing lots of things every frame

in C++ for example, structs are value types and support inheritance, which is nice, but I like Swift syntax more.

edit: i.e. I feel like this is cool for larger projects, but annoying for my own code that I fully understand.

instead of making the position a protocol requirement, make a generic type that stores a position, and delegate the variations to the type parameter

1 Like

The overhead is not huge (one extra line per entity type?) – I won't consider it too tiresome.

Although indeed it would be nice to have:

struct Foo {
    var a: Int
}
struct Bar: Foo {
    var b: Int
}
var bar = Bar(a: 1, b: 2)
bar.a = 3

instead of:

struct Foo {
    var a: Int
}
struct Bar {
    var foo: Foo
    var b: Int
}
var bar = Bar(foo: Foo(a: 1), b: 2)
bar.foo.a = 3

Beware of this behavioural change:

class Entity {
    var position: CGPoint
    init(_ p: CGPoint) {
        position = p
    }
}

var a = Entity(CGPoint(x: 1, y: 2))
var b = a
a.position = CGPoint(x: 3, y: 4)
print(b.position) // (3.0, 4.0) with class and (1.0, 2.0) with struct
1 Like