In what circumstances? I need to add sending
on with inout
?
any examples?
In what circumstances? I need to add sending
on with inout
?
any examples?
inout
isn’t exactly like passing a value in and getting a return value, but it’s pretty close. So f(x: inout sending X)
is a lot like f(x: sending X) -> sending X
, which is a pretty reasonable thing.
@jrose Would you mind giving an example? I'm also trying to understand what's the use case for inout sending
. I have a working example, but find it's meaningless. In my current understanding inout
and sending
were desigend for conflicting requirements: inout
expects a function to copy back value on return, .sending
is used to send the mutatble value to another isolation domain, which implicitly means the changed value isn't available synchronously
class C {
var value = 0
}
struct S {
var c = C()
}
actor MyActor1 {
func foo(_ actor2: MyActor2, _ s: inout sending S) async {
await actor2.bar(&s)
s = S() // This is meaningless, but it's required to reassign a value to s.
}
}
actor MyActor2 {
func bar(_ s: inout S) {
s.c.value += 1
}
}
EDIT: sorry, I confused await
with Task
. MyActor1.foo(_:, _:)
can get the changed value before it returns. I think out a new version. I'm curious if there is a more elegant way?
actor MyActor1 {
func foo(_ actor2: MyActor2, _ s: inout sending S) async {
nonisolated(unsafe) var _s = s
await actor2.bar(&_s)
s = _s
}
}
I want to share a marvelous speech given by @FranzBusch at SwiftServerConf 2024, it contained an example talking about the motivation of using inout sending
. The relevant content is around 18:30.
I’m not sure it actually works yet, but there was a thread last year about using it to model accessing a Mutex-protected value: Sending, inout sending, Mutex
It is used in the standard library's Mutex
API, but you can't really use it with non-Sendable types due to bugs:
There's also missing language features such as an inability to define a sending
self
parameter which mean you have to resort to some pretty anti-idiomatic designs (static methods that take sending this: Self
as the first argument for example)
@KeithBauerANZ NOTE: the first one that you mention here is due to us needing run once closures. Without that the compiler does not technically know how many times the closure will be used so we have to be more conservative around the analysis. We could hack around the lack of such a feature today, but we would really just be creating a single adhoc version of such a feature.
Ah, of course. That comes up all the time with sending
and ~Copyable
types.
I haven't seen an evolution proposal for it (or a larger embedded-focused overhaul of closures) yet though — for me, this is one of the most common holes in the language that I step into. And the errors are always unhelpful — a simple (function xyz may call closure parameter more than once)
in addition to the regular sending
error would help so much!
You can fake it with a protocol.
protocol CallOnce: ~Copyable {
associatedtype Input
associatedtype Output
associatedtype Error: Swift.Error = Never
consuming func callAsFunction(_: Input) throws(Error) -> Output
}
This isn't perfect though (Input should be a parameter pack of potentially non-copyable types with arbitrary ownership per argument), and you need to create a new struct each time you want to call it, but at least that's macroable.