ifsheldon opened issue #9776:
Feature
There are no examples about using
bindgen!
withasync: true
, imports and resources in Rust hosts. Please add examples to do that.A bit of background:
I have a
string.wit
and a guest component.package component:big-string; interface large-string { resource largestring { constructor(); push: func(s: string); get: func() -> string; clear: func(); } } world big-string { import large-string; import print: func(s: string) -> bool; export manipulate-large-string: func() -> string; }
I want to run this component in a Rust host. It took me quite a while to debug and inspect the generated code with
cargo-expand
to implement this. The code is much more complicated than the and there's no way for IDE to help.I didn't have any clues how to write the code until I cargo-expanded the macro.
You should at least add an example in bindgen_example to give a first impression to newcomers about the complexity.
And probably you can simplify the traits of the async version with latest Rust async functions in traits.
Benefit
- Adding an example in bindgen_example can give a first impression to newcomers about the complexity
- Probably simplifying it in bindgen with latest Rust async functions in traits can ultimately remove these annoying lifetimes,
Pin
andBox
in function signatures, which will enable IDE to autocomplete and help better.Implementation
I can make a PR to submit my code to be an example.
Full Code
use crate::utils::get_component_linker_store; use crate::utils::{bind_interfaces_needed_by_guest_rust_std, ComponentRunStates}; use wasmtime::component::bindgen; use wasmtime::component::Resource; use wasmtime::Engine; mod sync_version { use super::*; bindgen!({ path: "../wit-files/string.wit", world: "big-string", with: { "component:big-string/large-string/largestring": LargeString } }); pub struct LargeString { pub storage: String, } impl BigStringImports for ComponentRunStates { fn print(&mut self, string: String) -> bool { println!("from print sync host func: {}", string); true } } impl component::big_string::large_string::Host for ComponentRunStates {} impl component::big_string::large_string::HostLargestring for ComponentRunStates { fn new(&mut self) -> Resource<LargeString> { self.resource_table .push(LargeString { storage: String::new(), }) .unwrap() } fn push(&mut self, resource: Resource<LargeString>, s: String) -> () { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.push_str(s.as_str()); } fn get(&mut self, resource: Resource<LargeString>) -> String { let large_string = self.resource_table.get(&resource).unwrap(); format!("sync: {}", large_string.storage) } fn clear(&mut self, resource: Resource<LargeString>) -> () { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.clear(); } fn drop(&mut self, resource: Resource<LargeString>) -> wasmtime::Result<()> { let _ = self.resource_table.delete(resource)?; Ok(()) } } } mod async_version { use futures::executor::block_on; use super::*; use core::future::Future; use core::pin::Pin; bindgen!({ path: "../wit-files/string.wit", world: "big-string", async: true, with: { "component:big-string/large-string/largestring": LargeString }, }); pub struct LargeString { pub storage: String, } impl BigStringImports for ComponentRunStates { fn print<'life, 'async_trait>( &'life mut self, s: String, ) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { // TODO: make a PR for this Box::pin(async move { println!("from print async host func: {}", s); true }) } } impl component::big_string::large_string::Host for ComponentRunStates {} impl component::big_string::large_string::HostLargestring for ComponentRunStates { fn new<'life, 'async_trait>( &'life mut self, ) -> Pin<Box<dyn Future<Output = Resource<LargeString>> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async { self.resource_table .push(LargeString { storage: String::new(), }) .unwrap() }) } fn push<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, s: String, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async move { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.push_str(s.as_str()); }) } fn get<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, ) -> Pin<Box<dyn Future<Output = String> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async move { let large_string = self.resource_table.get(&resource).unwrap(); format!("async: {}", large_string.storage) }) } fn clear<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async move { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.clear(); }) } fn drop<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, ) -> Pin<Box<dyn Future<Output = wasmtime::Result<()>> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async { let _ = self.resource_table.delete(resource)?; Ok(()) }) } } }
ifsheldon edited issue #9776:
Feature
There are no examples about using
bindgen!
withasync: true
, imports and resources in Rust hosts. Please add examples to do that.A bit of background
I have a
string.wit
and a guest component.package component:big-string; interface large-string { resource largestring { constructor(); push: func(s: string); get: func() -> string; clear: func(); } } world big-string { import large-string; import print: func(s: string) -> bool; export manipulate-large-string: func() -> string; }
I want to run this component in a Rust host. It took me quite a while to debug and inspect the generated code with
cargo-expand
to implement this. The code is much more complicated than the and there's no way for IDE to help.I didn't have any clues how to write the code until I cargo-expanded the macro.
You should at least add an example in bindgen_example to give a first impression to newcomers about the complexity.
And probably you can simplify the traits of the async version with latest Rust async functions in traits.
Benefit
- Adding an example in bindgen_example can give a first impression to newcomers about the complexity
- Probably simplifying it in bindgen with latest Rust async functions in traits can ultimately remove these annoying lifetimes,
Pin
andBox
in function signatures, which will enable IDE to autocomplete and help better.Implementation
I can make a PR to submit my code to be an example.
Full Code
use crate::utils::get_component_linker_store; use crate::utils::{bind_interfaces_needed_by_guest_rust_std, ComponentRunStates}; use wasmtime::component::bindgen; use wasmtime::component::Resource; use wasmtime::Engine; mod sync_version { use super::*; bindgen!({ path: "../wit-files/string.wit", world: "big-string", with: { "component:big-string/large-string/largestring": LargeString } }); pub struct LargeString { pub storage: String, } impl BigStringImports for ComponentRunStates { fn print(&mut self, string: String) -> bool { println!("from print sync host func: {}", string); true } } impl component::big_string::large_string::Host for ComponentRunStates {} impl component::big_string::large_string::HostLargestring for ComponentRunStates { fn new(&mut self) -> Resource<LargeString> { self.resource_table .push(LargeString { storage: String::new(), }) .unwrap() } fn push(&mut self, resource: Resource<LargeString>, s: String) -> () { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.push_str(s.as_str()); } fn get(&mut self, resource: Resource<LargeString>) -> String { let large_string = self.resource_table.get(&resource).unwrap(); format!("sync: {}", large_string.storage) } fn clear(&mut self, resource: Resource<LargeString>) -> () { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.clear(); } fn drop(&mut self, resource: Resource<LargeString>) -> wasmtime::Result<()> { let _ = self.resource_table.delete(resource)?; Ok(()) } } } mod async_version { use futures::executor::block_on; use super::*; use core::future::Future; use core::pin::Pin; bindgen!({ path: "../wit-files/string.wit", world: "big-string", async: true, with: { "component:big-string/large-string/largestring": LargeString }, }); pub struct LargeString { pub storage: String, } impl BigStringImports for ComponentRunStates { fn print<'life, 'async_trait>( &'life mut self, s: String, ) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { // TODO: make a PR for this Box::pin(async move { println!("from print async host func: {}", s); true }) } } impl component::big_string::large_string::Host for ComponentRunStates {} impl component::big_string::large_string::HostLargestring for ComponentRunStates { fn new<'life, 'async_trait>( &'life mut self, ) -> Pin<Box<dyn Future<Output = Resource<LargeString>> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async { self.resource_table .push(LargeString { storage: String::new(), }) .unwrap() }) } fn push<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, s: String, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async move { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.push_str(s.as_str()); }) } fn get<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, ) -> Pin<Box<dyn Future<Output = String> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async move { let large_string = self.resource_table.get(&resource).unwrap(); format!("async: {}", large_string.storage) }) } fn clear<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async move { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.clear(); }) } fn drop<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, ) -> Pin<Box<dyn Future<Output = wasmtime::Result<()>> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async { let _ = self.resource_table.delete(resource)?; Ok(()) }) } } }
fitzgen commented on issue #9776:
In general, adding an example of async
bindgen!
would be great!On the particulars of what the example is doing and whether your given example is best, I don't have super strong opinions. A timer might be smaller and more realistic? Not 100% clear to me.
Maybe @alexcrichton has opinions.
ifsheldon commented on issue #9776:
Thanks!
Regarding this long function signature, do you know any ways to simplify it? I thought the stabilized async functions in traits might help. I don't know if
async-trait
crate can help, either.-> Pin<Box<dyn Future<Output = SomeReturnType> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait
alexcrichton commented on issue #9776:
Definitely feel free to add an example with a PR, it would be most welcome!
All of wasmtime's async support was designed before async functions were available in traits, and yes nowadays we should remove the need for
#[async_trait]
ideally. That'll require using-> impl Future<...> + Send
in the trait definitions (can't just use rawasync fn
just yet I think), but the trait implementors can still useasync fn ...
in that case.If you're up for it a PR to remove
#[async_trait]
would be most welcome, but I suspect that would be a pretty involved PR as well.
ifsheldon commented on issue #9776:
Definitely feel free to add an example with a PR, it would be most welcome!
I will make a PR with a modified example that makes a bit more sense.
All of wasmtime's async support was designed before async functions were available in traits, and yes nowadays we should remove the need for #[async_trait] ideally
So, bindgen indeed uses
#[async_trait]
internally? I couldn't see this when I just didcargo-expand
which expands all macros recursively. If so, I think the users/implmentors can also leverage#[async_trait]
to simplify theimpl
block.That'll require using -> impl Future<...> + Send in the trait definitions (can't just use raw async fn just yet I think), but the trait implementors can still use async fn ... in that case.
We can use
trait_variant
mentioned in the Announcingasync fn
and return-positionimpl Trait
in traits so we don't even need to write-> impl Future<...> + Send
in bindgen's implementation ourselves. Just something like#[trait_variant::make(HostLargestring: Send)] pub trait LocalHostLargestring { async fn new(&mut self) -> Resource<LargeString>; // ... }
If you're up for it a PR to remove #[async_trait] would be most welcome, but I suspect that would be a pretty involved PR as well.
Yeah, this may take a while. I will just make a PR to add an example first. Then I will open another tracking issue for this.
alexcrichton commented on issue #9776:
Using
trait_variant
makes sense to me yeah! And yes bindings use#[async_trait]
which is why generated docs aren't great.
alexcrichton closed issue #9776:
Feature
There are no examples about using
bindgen!
withasync: true
, imports and resources in Rust hosts. Please add examples to do that.A bit of background
I have a
string.wit
and a guest component.package component:big-string; interface large-string { resource largestring { constructor(); push: func(s: string); get: func() -> string; clear: func(); } } world big-string { import large-string; import print: func(s: string) -> bool; export manipulate-large-string: func() -> string; }
I want to run this component in a Rust host. It took me quite a while to debug and inspect the generated code with
cargo-expand
to implement this. The code is much more complicated than the and there's no way for IDE to help.I didn't have any clues how to write the code until I cargo-expanded the macro.
You should at least add an example in bindgen_example to give a first impression to newcomers about the complexity.
And probably you can simplify the traits of the async version with latest Rust async functions in traits.
Benefit
- Adding an example in bindgen_example can give a first impression to newcomers about the complexity
- Probably simplifying it in bindgen with latest Rust async functions in traits can ultimately remove these annoying lifetimes,
Pin
andBox
in function signatures, which will enable IDE to autocomplete and help better.Implementation
I can make a PR to submit my code to be an example.
Full Code
use crate::utils::get_component_linker_store; use crate::utils::{bind_interfaces_needed_by_guest_rust_std, ComponentRunStates}; use wasmtime::component::bindgen; use wasmtime::component::Resource; use wasmtime::Engine; mod sync_version { use super::*; bindgen!({ path: "../wit-files/string.wit", world: "big-string", with: { "component:big-string/large-string/largestring": LargeString } }); pub struct LargeString { pub storage: String, } impl BigStringImports for ComponentRunStates { fn print(&mut self, string: String) -> bool { println!("from print sync host func: {}", string); true } } impl component::big_string::large_string::Host for ComponentRunStates {} impl component::big_string::large_string::HostLargestring for ComponentRunStates { fn new(&mut self) -> Resource<LargeString> { self.resource_table .push(LargeString { storage: String::new(), }) .unwrap() } fn push(&mut self, resource: Resource<LargeString>, s: String) -> () { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.push_str(s.as_str()); } fn get(&mut self, resource: Resource<LargeString>) -> String { let large_string = self.resource_table.get(&resource).unwrap(); format!("sync: {}", large_string.storage) } fn clear(&mut self, resource: Resource<LargeString>) -> () { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.clear(); } fn drop(&mut self, resource: Resource<LargeString>) -> wasmtime::Result<()> { let _ = self.resource_table.delete(resource)?; Ok(()) } } } mod async_version { use futures::executor::block_on; use super::*; use core::future::Future; use core::pin::Pin; bindgen!({ path: "../wit-files/string.wit", world: "big-string", async: true, with: { "component:big-string/large-string/largestring": LargeString }, }); pub struct LargeString { pub storage: String, } impl BigStringImports for ComponentRunStates { fn print<'life, 'async_trait>( &'life mut self, s: String, ) -> Pin<Box<dyn Future<Output = bool> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { // TODO: make a PR for this Box::pin(async move { println!("from print async host func: {}", s); true }) } } impl component::big_string::large_string::Host for ComponentRunStates {} impl component::big_string::large_string::HostLargestring for ComponentRunStates { fn new<'life, 'async_trait>( &'life mut self, ) -> Pin<Box<dyn Future<Output = Resource<LargeString>> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async { self.resource_table .push(LargeString { storage: String::new(), }) .unwrap() }) } fn push<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, s: String, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async move { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.push_str(s.as_str()); }) } fn get<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, ) -> Pin<Box<dyn Future<Output = String> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async move { let large_string = self.resource_table.get(&resource).unwrap(); format!("async: {}", large_string.storage) }) } fn clear<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, ) -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async move { let large_string = self.resource_table.get_mut(&resource).unwrap(); large_string.storage.clear(); }) } fn drop<'life, 'async_trait>( &'life mut self, resource: Resource<LargeString>, ) -> Pin<Box<dyn Future<Output = wasmtime::Result<()>> + Send + 'async_trait>> where 'life: 'async_trait, Self: 'async_trait, { Box::pin(async { let _ = self.resource_table.delete(resource)?; Ok(()) }) } } }
Last updated: Dec 23 2024 at 12:05 UTC