最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

swift - Weird mutating async behaviour: Cannot call mutating async function on actor-isolated property - Stack Overflow

programmeradmin1浏览0评论

Consider these giving structs:

//A struct with mutating async function
struct Model {
    mutating func doSometingAsyncMutating() async {
       //do something
    }

    mutating func doSomethingMutating() {
        //do something
    }
}

@MainActor
struct MyStruct {
    var model: Model {
        get { /* some code */ }
        nonmutating set { /* some code*/ }
    }

    func callModelAsyncMutatingFunction() async {
         await model.doSometingAsyncMutating() //Error: Cannot call mutating async function 'doSometingAsyncMutating()' on actor-isolated property 'model'
    }

    mutating func callModelAsyncMutatingFunctionWithMutating() async {
        await model.doSometingAsyncMutating() //ok
    }

    func callModelMutatingOnlyFunction() {
        model.doSomethingMutating() // ok
    }
}

As you can see, the setter of property model is nonmutating, but the function callModelAsyncMutatingFunction() still throw the error Cannot call mutating async function 'doSometingAsyncMutating()' on actor-isolated property 'model', otherwise mutating function callModelFunctionWithMutating() is ok.

And the funny thing is callModelMutatingOnlyFunction() is ok, because it's not a async function.

And if I remove the @MainActor from MyStruct, then all functions are ok:

// @MainActor
struct MyStruct {
    var model: Model {
        get { /* some code */ }
        nonmutating set { /* some code*/ }
    }

    func callModelAsyncMutatingFunction() async {
         await model.doSometingAsyncMutating() //ok
    }

    mutating func callModelAsyncMutatingFunctionWithMutating() async {
        await model.doSometingAsyncMutating() //ok
    }

    func callModelMutatingOnlyFunction() {
        model.doSomethingMutating() // ok
    }
}

My question is why the behaviour like that? It seems like there are many factors affecting one thing. And what I really try to achieve is like that:

@MainActor //I need @MainActor
struct MyStruct {
    var model: Model { // Adding nonioslated not an option
        get { /* some code */ }
        nonmutating set { /* some code*/ }
    }

    func callModelAsyncMutatingFunction() async { //Adding mutating not an option
         await model.doSometingAsyncMutating() //I need it to be ok
    }
}

I already checked a couple articles, like this, but not really helping, any idea?

Consider these giving structs:

//A struct with mutating async function
struct Model {
    mutating func doSometingAsyncMutating() async {
       //do something
    }

    mutating func doSomethingMutating() {
        //do something
    }
}

@MainActor
struct MyStruct {
    var model: Model {
        get { /* some code */ }
        nonmutating set { /* some code*/ }
    }

    func callModelAsyncMutatingFunction() async {
         await model.doSometingAsyncMutating() //Error: Cannot call mutating async function 'doSometingAsyncMutating()' on actor-isolated property 'model'
    }

    mutating func callModelAsyncMutatingFunctionWithMutating() async {
        await model.doSometingAsyncMutating() //ok
    }

    func callModelMutatingOnlyFunction() {
        model.doSomethingMutating() // ok
    }
}

As you can see, the setter of property model is nonmutating, but the function callModelAsyncMutatingFunction() still throw the error Cannot call mutating async function 'doSometingAsyncMutating()' on actor-isolated property 'model', otherwise mutating function callModelFunctionWithMutating() is ok.

And the funny thing is callModelMutatingOnlyFunction() is ok, because it's not a async function.

And if I remove the @MainActor from MyStruct, then all functions are ok:

// @MainActor
struct MyStruct {
    var model: Model {
        get { /* some code */ }
        nonmutating set { /* some code*/ }
    }

    func callModelAsyncMutatingFunction() async {
         await model.doSometingAsyncMutating() //ok
    }

    mutating func callModelAsyncMutatingFunctionWithMutating() async {
        await model.doSometingAsyncMutating() //ok
    }

    func callModelMutatingOnlyFunction() {
        model.doSomethingMutating() // ok
    }
}

My question is why the behaviour like that? It seems like there are many factors affecting one thing. And what I really try to achieve is like that:

@MainActor //I need @MainActor
struct MyStruct {
    var model: Model { // Adding nonioslated not an option
        get { /* some code */ }
        nonmutating set { /* some code*/ }
    }

    func callModelAsyncMutatingFunction() async { //Adding mutating not an option
         await model.doSometingAsyncMutating() //I need it to be ok
    }
}

I already checked a couple articles, like this, but not really helping, any idea?

Share Improve this question edited Feb 15 at 16:45 matt 536k93 gold badges932 silver badges1.2k bronze badges asked Feb 15 at 8:39 TylerTyler 546 bronze badges 6
  • Model is a struct when you mutate it is making a copy of the original and applying the new values. Try using actors for this type of stuff. – lorem ipsum Commented Feb 15 at 8:48
  • @loremipsum Then why is ok if I remove MainActor or call a not async but mutating function? – Tyler Commented Feb 15 at 9:09
  • Because then it is synchonous – lorem ipsum Commented Feb 15 at 9:16
  • @loremipsum but callModelAsyncMutatingFunctionWithMutating is asynchronous and it is ok – Tyler Commented Feb 15 at 9:35
  • 1 Not sure what you are trying to accomplish. Please clarify, why Model has mutating functions, yet you can use a non-mutating setter in MyStruct. How should this work? It seems, you are trying to trick the compiler to accept a construct (using the nonmutating key-word) to accomplish something which is not possible anyways because it contradicts itself. Please provide an example. :) Also, why does a Model has async functions anyway? These "Models" should be POD, i.e. data (which are composite, just data, plain data, no functions no behaviour, no async. :) – CouchDeveloper Commented Feb 16 at 11:26
 |  Show 1 more comment

1 Answer 1

Reset to default 0

I believe you're overthinking this. The problem is that in

func callModelAsyncMutatingFunction() async {
    await model.doSometingAsyncMutating() // *
}

... the term model in the starred line is effectively a let constant. Therefore it cannot be mutated. To mutate it, take a var copy:

func callModelAsyncMutatingFunction() async {
    var modelCopy = model
    await modelCopy.doSometingAsyncMutating()
    model = modelCopy // ?
}

You didn't explicitly say you wanted to set the mutated instance back into the self.model so I've put a question mark next to that line. But I presume it is the sort of thing you want to do.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论