One thing that feels off to me about this change is that it would imply to never mix async and streams in the same function signature. Which might be totally fine, but still.. :man_shrugging:
Agreed yeah, and it also makes me wonder if we should avoid using async in WASI, which feels a bit weird
or rather, feels like a weird conclusion
I think perhaps the general guideline here is: avoid async on resource methods that wait to process an entire stream since that prevents forward-and-forget use cases. For non-stream-taking functions or scenarios where forward-and-forget isn't desirable (where the stream would be a "child" of the resource and so you're not going to drop/transfer the resource until you're done with it), async is fine.
scenarios where forward-and-forget isn't desirable (..)
asyncis fine.
that'd still be unrepresentable in Rust (safely)
what about, for example, write-via-stream?
Ludea has marked this topic as resolved.
@Alex Crichton I think it is in the "fine" category b/c there isn't a resource involved (that you might want to drop/transfer)
Technically though it still doesn't allow forward-and-forget, right?
because the component always has to be in the middle?
Alex Crichton has marked this topic as unresolved.
(I think the resolution was accidental)
Hmm, maybe I'm confused. I thought the thing we're "forgeting" is the resource whose method is borrowing the resource for the duration of the async call?
Oh, true! I was referring additionally to the component iteslf (I think I crossed some wires here), but the component has to stay alive for async functions where for a sync function you could get your component out of the way entirely
whether that matters, however, is probably to a much lesser degree
(More specifically, "forget" means "drop the resource or transfer to another component (which might drop it immediately, you don't know)".)
FWIW, in a future with runtime-instantiation (where this does come up again), "resource-ifying" a component will "resource-ify" the interfaces exported by the component, turning free functions into methods of the instance-resource. But in this case, I think we actually need the "child" semantics because if the component-instance gets destroyed, it'll delete the actual implementation of the stream consumer (e.g., its linear memory state), so we can't be firing-and-forgetting.
depends on the implementation(?). If it uses reference counting, the component instance can stay until all its resources have been dropped
the WASI 0.3 interfaces have moved away from parent->child resource traps
Good point! This depends on how things get specified, but I was thinking that resource.drop would synchronously destroy the component instance (so that way resources are freed when you think they are (and if they can't, b/c you have "dangling" handles, you trap). Anyhow, farther future topic
the WASI 0.3 interfaces have moved away from parent->child resource traps
True, but that's due to the lack of a child annotation in WIT that allows bindings generators (specifically of GC languages) to do the thing they need to do to avoid the trap.
There is a somewhat orthogonal vector to all of this where what @Dave Bakker (badeend) you're doing is not pleasant with the default Rust bindings, so e.g. ergonomically we could in theory try to improve things without actually changing WITs
one example is that changing write-via-stream, which doesn't have resources involved, would actually probably be nice from a rust ergonomics perspective where it's easier to deal with concrete types rather than futures
but I wouldn't want rust ergonomics alone to guide decisions of API design in WASI of course
A concrete type instead of impl Future could help with ergonomics and is indeed a Rust-specific thing.
The issue at hand is about lifetimes though, and those are a consequence of the WIT signature.
Unless I'm misunderstanding what you're suggesting
oh sorry, I'm apparently a bit scatterbrained. I'm assuming, yes, we'll remove async from resource methods in WASI resources, and a general WASI principle will be no async methods without a good reason. That leaves write-via-stream for stdio, however, which is still async. Using that in the Rust standard library is probably going to be easier if it returned a WIT future as opposed to a Rust impl Future.
Although that being said libstd is probably going to use stdio through libc, so you can probably just ignore my musing
FWIW, my PR turns write-via-stream non-async as well.
But I've lost track in this chat whether the consensus is if that is a good thing or a bad thing
I believe consensus is: it's not necessary, but for rust ergonomics it may be nice
but this logic applies to all async functions everywhere, so I'm not sure if we should follow through with that
Also, to provide an entire counterpoint to this, it's kind of nice that the component model guarantees you can't close the resource while there's an active async method running on it. That way implementations don't have to handle the question of "someone called write, then closed the resource, what do I do?"
Agree. Although that doesn't extrapolate to the read side, where the stream is returned opposed to consumed
good point yeah
changing all resource methods to not be async I believe would require updating quite a lot of wasi:filesystem/types.descriptor functions too, but I'm also less sure that's a good idea
Last updated: Jan 29 2026 at 13:25 UTC