Trait HasData
pub trait HasData: 'static {
type Data<'a>;
}
Expand description
A trait used as part of bindgen!
to indicate a Data<'_>
payload that
implements some host bindings traits.
The purpose of the bindgen!
macro is to define Rust traits that the host
can implement to fulfill WIT functions imported from the host into a
component. The bindgen!
macro then additionally generates a function
which takes a Linker
and an implementation of the traits and fills out
the Linker
. This trait, HasData
, is used in this process of filling
out the Linker
for some WIT interfaces.
Wasmtime’s Store<T>
type is the home for all per-instance state.
Notably the T
here is generic (the Wasmtime library allows any type to be
placed here) and it’s also instance-specific as a Store<T>
is typically
allocated one-per-instance. Implementations of host APIs, however, often
want to live in a library and not be tied to any particular T
. For example
Wasmtime provides the wasmtime-wasi
crates as an implementation of
standard WASI APIs as a library, but they don’t want to fix a particular T
in Store<T>
as embedders should be able to fill out their own T
for
their needs. The purpose of this trait is to enable this situation.
This trait is used in add_to_linker
functions generated by bindgen!
in
conjunction with a function pointer. It looks something along the lines of:
use wasmtime::component::{Linker, HasData};
// generated by bindgen!
trait Host {
// ..
}
fn add_to_linker<T, D>(linker: &mut Linker<T>, getter: fn(&mut T) -> D::Data<'_>)
where D: HasData,
for<'a> D::Data<'a>: Host,
{
// ...
}
Here the D
type parameter, bounded by HasData
, is used to specify
the return type of the getter
function provided here. The getter
“projects” from &mut T
to D::Data<'_>
, notably enabling it to capture
the lifetime of the &mut T
passed in as well.
The Data
associated type here is further bounded in add_to_linker
above
as it must implement the traits generated by bindgen!
. This means that
linker
is filled out with functions that, when called, first getter
is
invoked and then the actual function delegates to the Host
trait
implementation in this case.
The D
type parameter here isn’t actually a runtime value, nor is it stored
anywhere. It’s purely used as a means of projecting a “generic associated
type”, here where <'a>
is the generic argument, a lifetime. This means
that the choice of D
is disconnected from both T
and D::Data<'_>
and
can be whatever you like. Sometimes you might need to define an empty struct
in your project to use this, but Wasmtime also provides a convenience
HasSelf
type to avoid the need for this in some common use cases.
§Example: Host for T
using Store<T>
Let’s say you wanted to invoke the above add_to_linker
function where the
T
in Store<T>
directly implements the Host
trait itself:
use wasmtime::component::{Linker, HasSelf};
struct MyStoreState { /* ... */ }
impl Host for MyStoreState {
// ..
}
fn fill_out_my_linker(linker: &mut Linker<MyStoreState>) {
add_to_linker::<_, HasSelf<_>>(linker, |x| x)
}
Here the add_to_linker
invocation is annotated with <_, HasSelf<_>>
. The
first argument gets inferred to MyStoreState
, and the second argument gets
inferred to HasSelf<MyStoreState>
This means that the projection function
in this case, here the identity |x| x
, is typed as
fn(&mut MyStoreState) -> &mut MyStoreState
. This is because the HasData
implementation for HasSelf<T>
means that we have
type Data<'a> = &'a mut T
.
§Example: Host for MyLibraryState
using Store<T>
Let’s say though that you instead are writing a library like WASI where you
don’t know the T
of Store<T>
ahead of time. In such a case you might
hand-write your own add_to_linker
wrapper that looks like this:
use wasmtime::component::{Linker, HasData};
// publicly exposed per-instance state for your library, users will
// construct this and store it within the `T` in `Store<T>`
pub struct MyLibraryState { /* ... */ }
impl Host for MyLibraryState {
// ..
}
// hand-written publicly exposed convenience function to add this crate's
// component functionality to the provided linker.
pub fn add_my_library_to_linker<T>(
linker: &mut Linker<T>,
f: fn(&mut T) -> &mut MyLibraryState,
) {
// invoke the bindgen!-generated `add_to_linker`
add_to_linker::<_, MyLibrary>(linker, f);
}
// Note this need not be publicly exposed, it's just a private internal
// detail.
struct MyLibrary;
impl HasData for MyLibrary {
type Data<'a> = &'a mut MyLibraryState;
}
Here the MyLibrary
type, private to this crate, has a HasData
implementation which indicates that implementations of the
bindgen!
-generated APIs are done in terms of MyLibraryState
specifically. The add_my_library_to_linker
takes an externally-provided
f
closure which projects from &mut T
, whatever data the store is using,
to MyLibraryState
which must be stored within the store itself.
§Example: Host for MyLibraryState<'_>
using Store<T>
Let’s say you’re like the above scenario where you’re writing a library but
instead of being able to wrap up all your state for each trait
implementation in a structure it’s spread across a few structures. These
structures may be stored in different locations inside of Store<T>
which
makes it difficult to wrap up in a single type and return that. Here you can
make use of “view” types to collect a number of disjoint borrows into one
structure:
use wasmtime::component::{Linker, HasData};
// Like before, this is publicly exposed, and this is a "bag of pointers" to
// all the pieces of state necessary to implement `Host` below.
pub struct MyLibraryState<'a> {
pub state_a: &'a mut StateA,
pub state_b: &'a mut StateB,
}
impl Host for MyLibraryState<'_> {
// ..
}
pub fn add_my_library_to_linker<T>(
linker: &mut Linker<T>,
f: fn(&mut T) -> MyLibraryState<'_>,
) {
// invoke the bindgen!-generated `add_to_linker`
add_to_linker::<_, MyLibrary>(linker, f);
}
struct MyLibrary;
impl HasData for MyLibrary {
type Data<'a> = MyLibraryState<'a>;
}
This is similar to the above example but shows using a lifetime parameter on a structure instead of using a pointer-with-a-lifetime only. Otherwise though this’ll end up working out the same way.
§Example: Host for U: MyLibrary
using Store<T>
Let’s say you’re in a situation where you’re a library which wants to create
a “simpler” trait than the Host
-generated traits from bindgen!
and
then you want to implement Host
in terms of this trait. This requires a
bit more boilerplate as well as a new custom structure, but doing so looks
like this:
use wasmtime::component::{Linker, HasData};
// This is your public trait which external users need to implement. This
// can encapsulate any "core" functionality needed to implement
// bindgen!-generated traits such as `Host` below.
pub trait MyLibraryTrait {
// ...
}
// You'll need to provide a "forwarding" implementation of this trait from
// `&mut T` to `T` to get the below implementation to compile.
impl<T: MyLibraryTrait + ?Sized> MyLibraryTrait for &mut T {
// ...
}
// This is a bit of a "hack" and an unfortunate workaround, but is currently
// used to work within the confines of orphan rules and such. This is
// publicly exposed as the function provided below must provide access to
// this.
pub struct MyLibraryImpl<T>(pub T);
// Here you'd implement all of `Host` in terms of `MyLibraryTrait`.
// Functions with `&mut self` would use `self.0` to access the functionality
// in `MyLibraryTrait`.
impl<T: MyLibraryTrait> Host for MyLibraryImpl<T> {
// ..
}
// optional: this avoids the need for `self.0` accessors in `Host` above,
// but this otherwise isn't required.
impl<T: MyLibraryTrait> MyLibraryTrait for MyLibraryImpl<T> {
// ..
}
// Note the second type parameter on this method, `U`, which is the user's
// implementation of `MyLibraryTrait`. Note that this additionally must
// be bounded with `'static` here.
pub fn add_my_library_to_linker<T, U>(
linker: &mut Linker<T>,
f: fn(&mut T) -> MyLibraryImpl<&mut U>,
)
where U: MyLibraryTrait + 'static,
{
add_to_linker::<_, MyLibrary<U>>(linker, f);
}
// An adjusted definition of `MyLibrary` relative to the previous example,
// still private, which hooks up all the types used here.
struct MyLibrary<U>(U);
impl<U: 'static> HasData for MyLibrary<U> {
type Data<'a> = MyLibraryImpl<&'a mut U>;
}
This iteration of implementing component-interfaces-as-a-library is unfortunately relatively verbose and unintuitive at this time. We’re always keen on improving this though, so if you’ve got ideas of how to improve this please let us know!
Required Associated Types§
type Data<'a>
type Data<'a>
The data associated with this trait implementation, chiefly used as a
generic associated type to allow plumbing the 'a
lifetime into the
definition here.
See the trait documentation for more examples.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.