Law of Exclusivity: Violation or not with class type

Hi! Question about the law of exclusivity.

If I have a struct that has a mutating function and takes a closure, I know it is illegal to mutate the struct from within the closure. AKA:

struct User {
   var name = "Roo"

  mutating func setName(_ name: String, callback: () -> Void) {
    self.name = name
    callback()
  }
}

var u = User()
u.setName("Kanga") { u.name = "haha" }

This will (correctly) generate a runtime exclusivity violation.

So far so good. If I make User a class instead of a struct and remove the mutating attribute, no error will happen at runtime.

My question is, is this just because it couldn't detect a violation or is it not considered a violation with a reference type because there is no well defined mutation duration.

Thanks for your help,
Ray

The individual mutations to name in your example are disjoint and don't conflict with each other. The exclusivity conflict arises because the call to setName, as a mutating method on a value type, is considered to be a mutation of the entire variable u, which then conflicts with the mutation of u.name in the closure. There's no equivalent rule for the "entire class" with reference types: exclusivity is applied to all the stored properties individually. That means that if User were a class type, the only mutations would be those non-overlapping mutations to name, so there wouldn't be a conflict.

I'd expect the exclusivity violation in your example to be diagnosed statically, by the way.

3 Likes

I'm guessing this is in the context of a REPL/playground, so Ray's probably seeing runtime violations instead.

1 Like

Thank you so much for the detailed explanation. As has been pointed out, I was in a playground which is why the error was caught at runtime.

Yes, thanks Kyle.

For those who are interested, this is diagnosed at run time because the closure here names a global variable rather than capturing a local variable.

var u = User()
u.setName("Kanga") { u.name = "haha" }

It's an unfortunate distinction between top-level code and code within a function body. To diagnose this statically, the compiler would need to analyze all closure bodies even though they don't mutably capture anything. It's possible to do this, but I'd much rather just not have top-level variable declarations treated like globals--that will always be a source of confusion where the compiler behavior is significantly different.

7 Likes