2

I am not sure when use nonisolated with stored properties of actor.

P.s In this blog Douglas Gregor asking same question "So... just always require nonisolated let for synchronous access?". But I don't understand his answer.

Who can explain please ?

Edit:

Clarification, as it is written in the blog, using nonisolated helps to get rid of problems in the future if the property becomes mutable, but then it turns out that any immutable property must be noniolsated, because no one knows how their program will change over time

  • Why don't you ask him there? Surely if someone posts an answer on a public forum, you are more likely to receive an explanation when asking from OP directly on said forum rather than trying to ask on a completely independent forum, which OP might not even see. – Dávid Pásztor Mar 06 '23 at 14:19
  • @DávidPásztor Thanks for the advice. I will do so, but I am sure that they will answer me faster here – Pogos Anesyan Mar 06 '23 at 14:49

2 Answers2

1

Properties (let or computed var) and methods can be made nonisolated when you want to make them accessible outside the actor without having to use await keyword when calling them. I have been looking at this recently and this is a good explanation with examples.

https://www.hackingwithswift.com/quick-start/concurrency/how-to-make-parts-of-an-actor-nonisolated

https://www.avanderlee.com/swift/nonisolated-isolated/

I have used mine nonisolated but in a simple test I have just run it seems to work with a let whether nonisolated or not. I do notice that when the let is not specified with nonisolated I get a warning in the auto complete though, hence why I have been specifying nonisolated. I expect there may be problems in future by not specifying.

enter image description here

enter image description here

Here is the simple code and the view works in both cases it seems.

actor Foo {
nonisolated let test = "Test String"
}

class Bar: ObservableObject {
let foo = Foo()
@Published var localString: String = ""

func setString() {
    // Accessing the nonisolated let. A warning is shown in auto complete
    // if not specified nonisolated in autocompelte.
    localString = foo.test
}
}


struct Test2: View {
@ObservedObject var bar = Bar()

var body: some View {
    
    VStack {
        Text("Bar local string = \(bar.localString)")
        Button("Set") {
            bar.setString()
        }
        Button("Clear") {
            bar.localString = ""
        }
    }
}
}
Hongtron
  • 217
  • 6
  • I agree with you! But you don't actually need to use nonisolated with immutable properties for getting access outside actor. In blog that I mentioned in my question specified that you use nonisolated for avoiding problem in future when you need refactor from var to let. And my question is: do I need to use nonisolated every time with let ? – Pogos Anesyan Mar 06 '23 at 18:01
  • I have updated my post with some more details from my experience. – Hongtron Mar 06 '23 at 18:52
  • If the let is not being used outside the actor it would be good practice to mark it private to make sure. I had not noticed until I tested that I could use the let synchronously even though it was not nonisolated. The only warning was in the code complete, a little worrying for the future if not careful. – Hongtron Mar 06 '23 at 20:19
1

You said:

as it is written in the blog, using nonisolated helps to get rid of problems in the future if the property becomes mutable

The nonisolated let does not solve this problem. (One can argue that it contributes to this problem, requiring that if you need/want to enjoy synchronous access within the module, that you also make an explicit contract to that effect to external modules, which may or may not be desired.)

If you use nonisolated let, and later change it to a mutable var, this will require refactoring of code currently accessing this property synchronously to now do so asynchronously. This proposed change to the language is suggesting a potential mechanism to somewhat mitigate this.

Specifically, it proposed that in the absence of the nonisolated qualifier on a constant, that the module still enjoy the benefits of synchronous (non-isolated) access. But it is also proposing that outside this module, that it would remain isolated (which, while you would not enjoy that synchronous interface, would mean that a subsequent introduction of a mutable state would not necessarily break its interface).

You observed:

In this blog Douglas Gregor asking same question “So... just always require nonisolated let for synchronous access?”

He is not advocating that position. It is merely a strawman, an acknowledgment of the state of affairs at that point in time. In that section, he outlined a few reasons why marking all constants with nonisolated let may not be prudent.

That is why he is proposing (in the next section) that a simple let allow synchronous access within the module, but not across modules.

This is the behavior we currently see in Swift.

You ask:

do I need to use nonisolated every time with let?

You should only use nonisolated let where both of the following are true:

  • you have a legitimate requirement for synchronous, non-isolated, access; and

  • where you are reasonably confident that you are unlikely to ever make it mutable and/or, should you need to make this change in the future, that you (and any other developers using this type) are willing to sign up for the refactoring exercise that may entail.

As a general matter, we want to avoid code-breaking interface changes as an implementation evolves. So, where necessary, certainly feel free to make your constants nonisolated, but otherwise, you probably want to refrain from doing so.

You ask:

I am not sure when use nonisolated with stored properties of actor.

It should be noted that we do not need to use nonisolated qualifier within a module anymore to enjoy synchronous, cross-actor access. As SE-0306 says:

… a cross-actor reference to immutable state is allowed from anywhere in the same module as the actor is defined because, once initialized, that state can never be modified (either from inside the actor or outside it), so there are no data races by definition.

So, you now only really need to use nonisolated let where you want to allow synchronous access to other modules and you are signing up for the implications of that contract.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I understood your answer to my question, "do I need to use nonisolated every time with let?". Thank you! But I cannot agree with: "If you use nonisolated let, and later change it to a var, this will require refactoring of external code accessing this property synchronously to now do so asynchronously." If we had an actor with immutable nonisolated property and we decided to make it var, then we will have to make it computed. Since this property was immutable, all requests were readonly, as a consequence, moving will not break the client code. – Pogos Anesyan Mar 07 '23 at 09:48
  • 1
    @PogosAnesyan - The whole point of that proposal to which you linked is the concern that, at some future point in time, should you need to replace a non-isolated, immutable, property (i.e., `nonisolated let`) with one that is mutable/isolated (i.e., `var`). Computed properties are not really relevant to that discussion. (And, as an aside, you can’t have a computed property that pierces the veil of actor isolation in a synchronous manner, anyway, so it is somewhat moot, anyway.) – Rob Mar 07 '23 at 10:53