Class only protocol - why the examples are not working?

Thanks all for the discussion. For people who are still confused by the issue, below is my summary.

1) What this issue is (or: why example 2 and 3 don't work)

First some background:

  • For reference type, self is immutable. So it's impossible for an instance method to assign a new instance to self (compiler would throw an error if we did that).

  • For value type, however, that is fine. A mutating func can overwrite the entire structure.

The issue arises when we mix class and protocol which has mutating method.

  • When we define a protocol without adding a AnyObject contraint, Swift has no idea that the protocol is for class only. As a result, if the method modifies the instance's members, it has to be defined as mutating.

    Unfortunately defining a func as mutating gives it more power than we want - we may just want to let it modify instance members, but potentially it can modifiy self. So, even the actual func doesn't modify self, Swift compiler has to prepare for the worst case and assume the func modifies self.

  • That's the reason why the issue arises when we define a class and let the class conform to that protocol. The mutating method defined in the protocol can modify self! But this shouldn't occur if the instance variable is read-only. This is an unexpected issue because issue like this will never ever occur if we use class without protocol (see item 1 in background).

2) Why example 1 and 4 work

It's because we define Proto2 to conform to AnyObject. I guess that constraint makes the compiler processes the protocol's methods in a similar way as it processes class's methods (for example, throw error if a method tries to overwrite self). Since the compiler has made sure that it's impossible for the methods to overwrite self, it's fine.

3) How to work around it

  • Approach 1: make Proto2 to conform to AnyObject

  • Approach 2: using nonmutating keyword. See details in @Jumhyn's answer.

4) More thoughts

In general, what concern me is that if it's good idea to refactor a Swift project in protocol-oriented programming way (note that the project has to use some class types). It feels like that class and protocol don't play well in general and there are a lot of pitfalls. Some are solvable, some are probably not. For example, @jrose made the following comment in this thread:

Even beyond this, mutating and classes don't play so well together. It may not be the best model for what you're trying to do.

I wonder what are people's experience on using class type and protocol-oriented programming in general? Are there any best practice on this?

2 Likes