I thought a little about this in terms of modeling... would love all of your thoughts. I first have a section called the model and then below that I talk about implications in another small section
A Potential Model
Generally the idea with an inout parameter is that it can be viewed as an amalgamation of an @in and an @out parameter... creating our two conditions: the value must be initialized on entrance and on return must be initialized as well... one can reinitialize the inout by moving its contents out, reinitializing, moving out again, etc as long as one has an initialized value of the appropriate type in the inout binding upon function exit.
If one follows that model for transferring inout that would say to me that the inout can not just only have a value at exit, but that value must also be disconnected just like a transferring return value must be. Inside the function one would be able to merge the transferring parameter with actor isolated callees, send it off to other functions, reinitialize it with a newly constructed value (reinitializing it to a disconnected state) as long as one has a disconnected initialized value in the inout binding upon function exit. Implementing this would require a small tweak to the checker to check that a transferring inout value is not actor isolated at function exits.
In contrast, a disconnected inout value would be significantly stricter. Assigning to a disconnected inout value in the function body would act as a transfer to ensure that we preserve the disconnected property. To merge it into an actor region would act as a transfer instead of a merge:
actor MyActor {
var field: NonSendable
func passDisconnected(_ x: disconnected inout NonSendable) async {
let combine = (x, field) // This would have to be a transfer
x = T() // Reinitialize T since I moved it.
}
}
Of course upon exit, the disconnected inout value would need to have the same property as a transferring inout... namely that the value in the inout must be in a disconnected region. The difference in between the two comes down to what restrictions do we want to place upon it in the function body.
Interestingly even with transferring inout, using the model I laid out above, one has the property that passing a value as inout transferring ensures that the passed in var is still disconnected in the caller upon function return... even if the inout transferring value is passed into an actor method!:
actor MyActor {
var field: NonSendable
func useInOutTransferring(_ x: transferring inout NonSendable) async {
// Transfer the value into the actor.
field = x
// Must reinitialize x with a disconnected value before return.
x = NonSendable()
}
}
func performWork() async {
let a = MyActor()
let b = OtherActor()
var x = NonSendable()
// Without transferring, x would be transferred into actor's region since its value
// could be placed into the actor storage. One would have to reinitialize it before
// using it again. But since it is transferring, we can use x later and even transfer
// x later again.
await a.useInOutTransferring(&x)
await b.otherActor(&x)
}
Implications
My main thought here is it depends on what semantics we want to have here. I think both models could work. I haven't thought about which one would be the most ergonomic/etc (just trying to explore the state space) or if the state space is larger than what I put out here.