pub trait GetHost<T>:
Fn(T) -> Self::Host
+ Send
+ Sync
+ Copy
+ 'static {
type Host;
}
runtime
and component-model
only.Expand description
An “alias trait” used for bindgen!
-generated
add_to_linker_get_host
-style functions.
This trait is used as a trait bound for closures that “project” from a
store’s &mut T
internals to U
where U
is allowed to close over the
lifetime of &mut T
. This is a somewhat tricky operation to write down in a
trait bound which requires a somewhat special definition of this trait.
This trait has a supertrait of Fn(T) -> U
where U
here is specified as
an associated type Host
for this trait. There is then additionally a
blanket implementation for all Fn(T) -> U
closures as well meaning no
embedders should need to implement this trait manually.
Note that this trait additionally requires that closures are Send + Sync + Copy + 'static
which means they effectively need to behave like function
pointers.
Also note that using this trait requires type-annotations at this time. Refer to the example below for more information.
§Examples
Embedders should generally not need to write GetHost
bounds themselves but
they will need to interact with functions that have a parameter bounded by
GetHost
generated by bindgen!
.
use wasmtime::component::{Linker, GetHost};
// generated by `bindgen!`
trait Host { /* ... */ }
// generated by `bindgen!`
impl<'a, T: ?Sized + Host> Host for &'a mut T { /* ... */ }
// generated by `bindgen!`
fn add_to_linker_get_host<T, G>(linker: &mut Linker<T>, get_host: G)
where G: for<'a> GetHost<&'a mut T, Host: Host>,
{
// ...
}
// In some cases you'll implement `Host` for the `T` in the store directly.
struct MyType;
impl Host for MyType {
// ...
}
fn my_add_to_linker(linker: &mut Linker<MyType>) {
let get_host = type_annotate(|x| x);
add_to_linker_get_host(linker, get_host);
// Type annotating a closure is not the most intuitive thing to do in
// Rust, but that's all this function is doing. This is taking a `F`
// and then simply returning it, but the purpose of this function is to
// synthetically place a bound on `F` here with a `where` clause. This
// `where` clause is what annotates the type of `F` manually and is how
// we accurately describe the lifetimes associated with this closure.
fn type_annotate<F>(f: F) -> F
where F: Fn(&mut MyType) -> &mut MyType
{
f
}
}
// In other cases you might store the structure implementing `Host`
// elsewhere in the store
struct MyContainerType {
the_impl: MyType,
}
fn my_add_to_linker_with_container(linker: &mut Linker<MyContainerType>) {
let get_host = type_annotate(|x| &mut x.the_impl);
add_to_linker_get_host(linker, get_host);
fn type_annotate<F>(f: F) -> F
where F: Fn(&mut MyContainerType) -> &mut MyType
{
f
}
}
// And in some cases you might want to combine pieces of state within a `T`
// of a `Store` into one type that implements this state. This is, for
// example, how WASI impls work in Wasmtime.
struct MyStoreState {
the_table: MyTable,
the_impl: MyType,
}
struct MyTable { /* ... */ }
struct MyBorrowedState<'a> {
the_table: &'a mut MyTable,
the_impl: &'a mut MyType,
}
impl Host for MyBorrowedState<'_> {
// ...
}
fn my_add_to_linker_with_more_lifetimes(linker: &mut Linker<MyStoreState>) {
let get_host = type_annotate(|x| MyBorrowedState {
the_table: &mut x.the_table,
the_impl: &mut x.the_impl,
});
add_to_linker_get_host(linker, get_host);
fn type_annotate<F>(f: F) -> F
where F: Fn(&mut MyStoreState) -> MyBorrowedState<'_>
{
f
}
}
§Too Much Detail
The general idea behind this trait is that it expresses the ability to take
a closure as a parameter which projects from a store’s state, T
, to a U
which implements the bindgen-generated traits. The actual operation is
provided a &'a mut T
at runtime and U
must then also be able to close over
the 'a
too.
This is surprisingly subtle to do in Rust and there are many variations of writing this down which don’t achieve the desired result. For example the most naive way to possibly write this does not work.
use wasmtime::component::Linker;
// Naively let's just take `F: FnMut...`
fn add_to_linker_get_host<F, T, U>(linker: &mut Linker<T>, get_host: F)
where F: FnMut(&mut T) -> U,
{
// ...
}
struct MyStoreType { the_impl: MyType, }
struct MyType { /* ... */ }
impl Host for MyType { /* ... */ }
fn my_add_to_linker(linker: &mut Linker<MyType>) {
let get_host = type_annotate(|x| x);
add_to_linker_get_host(linker, get_host);
//~^ ERROR: one type is more general than the other
fn type_annotate<F>(f: F) -> F
where F: Fn(&mut MyType) -> &mut MyType
{
f
}
}
The problem with this version is that the exact definition of the trait
bound is: where F: for<'a> FnMut(&'a mut T) -> U
, and U
doesn’t have
access to the lifetime 'a
that’s only defined under the trait bound of F
itself. So this doesn’t work but how about lifting the lifetime up?
use wasmtime::component::Linker;
fn add_to_linker_get_host<'a, F, T: 'a, U>(linker: &mut Linker<T>, get_host: F)
where F: FnMut(&'a mut T) -> U + Send + Sync + 'static,
{
linker.root().func_wrap("f", move |mut store, ()| {
let mut data = store.data_mut();
let the_u = get_host(&mut data);
//~^ ERROR: borrowed data escapes outside of closure
Ok(())
});
}
The problem with this invocation is the meaning is still wrong. While the
above example will compile with 'a
defined here the actual usage of
get_host
doesn’t work. The root of the issue is that the lifetime can’t
actually be named when add_to_linker_get_host
is called, instead it must
be part of the for<'a>
part.
Rust doesn’t have higher-ranked definitions such as for<'a, U> Fn(&'a mut T) -> U
where U
would have access to the 'a
lifetime here, meaning that
at this point we’ve exhausted the various possibilities of using Fn
trait
bounds directly. They all mean subtly different things than the original
problem we’re trying to solve here.
This trait definition exists to accurately express in the trait system what
we’re trying to solve which is that the return value of a closure is allowed
to close over its arguments. This is achieved where the trait here is
defined with just T
, notably not &'a mut T
, meaning that whatever is in
scope for T
is also in-scope for the returned value from the closure.
Usage of this trait then looks like G: for<'a> GetHost<&'a mut T>
meaning
that the G::Host
associated type has access to 'a
.
This then gives rise to the second issue of this trait which is actually defining a closure that implements this trait. Ideally what we want to write is something that looks like this:
let my_closure = for<'a> |x: &'a mut MyStoreType| -> &'a mut MyType {
&mut x.the_impl
};
This syntax technically compiles but is unstable. Alternatively one might want to write:
let my_closure: impl FnMut(&mut MyStoreType) -> &mut MyType = |x| {
&mut x.the_impl
};
but this syntax is also unstable. Thus what we’re left with is the sort of kludge workaround in the examples above:
let my_closure = type_annotate(|x| &mut x.the_impl);
fn type_annotate<F>(f: F) -> F
where F: Fn(&mut MyStoreType) -> &mut MyType
{
f
}
Our hope is that this can get more ergonomic over time, but this is hopefully at least illuminating how we have ended up here.
Required Associated Types§
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.