pub struct ExternRef { /* private fields */ }
Expand description
An opaque, GC-managed reference to some host data that can be passed to WebAssembly.
The ExternRef
type represents WebAssembly externref
values. Wasm can’t
do anything with the externref
s other than put them in tables, globals,
and locals or pass them to other functions (such as imported functions from
the host). Unlike anyref
s, Wasm guests cannot directly allocate new
externref
s; only the host can.
You can use ExternRef
to give access to host objects and control the
operations that Wasm can perform on them via what functions you allow Wasm
to import.
Like all WebAssembly references, these are opaque and unforgeable to Wasm:
they cannot be faked and Wasm cannot, for example, cast the integer
0x12345678
into a reference, pretend it is a valid externref
, and trick
the host into dereferencing it and segfaulting or worse.
Note that you can also use Rooted<ExternRef>
and
ManuallyRooted<ExternRef>
as a type parameter with
Func::typed
- and
Func::wrap
-style APIs.
§Example
let engine = Engine::default();
let mut store = Store::new(&engine, ());
// Define some APIs for working with host strings from Wasm via `externref`.
let mut linker = Linker::new(&engine);
linker.func_wrap(
"host-string",
"new",
|caller: Caller<'_, ()>| -> Result<Rooted<ExternRef>> {
ExternRef::new(caller, Cow::from(""))
},
)?;
linker.func_wrap(
"host-string",
"concat",
|mut caller: Caller<'_, ()>, a: Rooted<ExternRef>, b: Rooted<ExternRef>| -> Result<Rooted<ExternRef>> {
let mut s = a
.data(&caller)?
.downcast_ref::<Cow<str>>()
.ok_or_else(|| Error::msg("externref was not a string"))?
.clone()
.into_owned();
let b = b
.data(&caller)?
.downcast_ref::<Cow<str>>()
.ok_or_else(|| Error::msg("externref was not a string"))?;
s.push_str(&b);
ExternRef::new(&mut caller, s)
},
)?;
// Here is a Wasm module that uses those APIs.
let module = Module::new(
&engine,
r#"
(module
(import "host-string" "concat" (func $concat (param externref externref)
(result externref)))
(func (export "run") (param externref externref) (result externref)
local.get 0
local.get 1
call $concat
)
)
"#,
)?;
// Create a couple `externref`s wrapping `Cow<str>`s.
let hello = ExternRef::new(&mut store, Cow::from("Hello, "))?;
let world = ExternRef::new(&mut store, Cow::from("World!"))?;
// Instantiate the module and pass the `externref`s into it.
let instance = linker.instantiate(&mut store, &module)?;
let result = instance
.get_typed_func::<(Rooted<ExternRef>, Rooted<ExternRef>), Rooted<ExternRef>>(&mut store, "run")?
.call(&mut store, (hello, world))?;
// The module should have concatenated the strings together!
assert_eq!(
result.data(&store)?.downcast_ref::<Cow<str>>().unwrap(),
"Hello, World!"
);
Implementations§
source§impl ExternRef
impl ExternRef
sourcepub fn new<T>(context: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>>
pub fn new<T>(context: impl AsContextMut, value: T) -> Result<Rooted<ExternRef>>
Creates a new instance of ExternRef
wrapping the given value.
The resulting value is automatically unrooted when the given context
’s
scope is exited. See Rooted<T>
’s documentation for
more details.
This method will not automatically trigger a GC to free up space in the GC heap; instead it will return an error. This gives you more precise control over when collections happen and allows you to choose between performing synchronous and asynchronous collections.
§Errors
If the allocation cannot be satisfied because the GC heap is currently
out of memory, but performing a garbage collection might free up space
such that retrying the allocation afterwards might succeed, then a
GcHeapOutOfMemory<T>
error is returned.
The GcHeapOutOfMemory<T>
error contains the host value that the
externref
would have wrapped. You can extract that value from this
error and reuse it when attempting to allocate an externref
again
after GC or otherwise do with it whatever you see fit.
§Example
let mut store = Store::<()>::default();
{
let mut scope = RootScope::new(&mut store);
// Create an `externref` wrapping a `str`.
let externref = match ExternRef::new(&mut scope, "hello!") {
Ok(x) => x,
// If the heap is out of memory, then do a GC and try again.
Err(e) if e.is::<GcHeapOutOfMemory<&'static str>>() => {
// Do a GC! Note: in an async context, you'd want to do
// `scope.as_context_mut().gc_async().await`.
scope.as_context_mut().gc();
// Extract the original host value from the error.
let host_value = e
.downcast::<GcHeapOutOfMemory<&'static str>>()
.unwrap()
.into_inner();
// Try to allocate the `externref` again, now that the GC
// has hopefully freed up some space.
ExternRef::new(&mut scope, host_value)?
}
Err(e) => return Err(e),
};
// Use the `externref`, pass it to Wasm, etc...
}
// The `externref` is automatically unrooted when we exit the scope.
sourcepub fn new_manually_rooted<T>(
store: impl AsContextMut,
value: T,
) -> Result<ManuallyRooted<ExternRef>>
pub fn new_manually_rooted<T>( store: impl AsContextMut, value: T, ) -> Result<ManuallyRooted<ExternRef>>
Creates a new, manually-rooted instance of ExternRef
wrapping the
given value.
The resulting value must be manually unrooted, or else it will leak for
the entire duration of the store’s lifetime. See
ManuallyRooted<T>
’s documentation for more
details.
§Errors
This function returns the same errors in the same scenarios as
ExternRef::new
.
§Example
let mut store = Store::<()>::default();
// Create a manually-rooted `externref` wrapping a `str`.
let externref = ExternRef::new_manually_rooted(&mut store, "hello!")?;
// Use `externref` a bunch, pass it to Wasm, etc...
// Don't forget to explicitly unroot the `externref` when you're done
// using it!
externref.unroot(&mut store);
sourcepub fn data<'a, T>(
&self,
store: impl Into<StoreContext<'a, T>>,
) -> Result<&'a (dyn Any + Send + Sync)>where
T: 'a,
pub fn data<'a, T>(
&self,
store: impl Into<StoreContext<'a, T>>,
) -> Result<&'a (dyn Any + Send + Sync)>where
T: 'a,
Get a shared borrow of the underlying data for this ExternRef
.
Returns an error if this externref
GC reference has been unrooted (eg
if you attempt to use a Rooted<ExternRef>
after exiting the scope it
was rooted within). See the documentation for
Rooted<T>
for more details.
§Example
let mut store = Store::<()>::default();
let externref = ExternRef::new(&mut store, "hello")?;
// Access the `externref`'s host data.
let data = externref.data(&store)?;
// Dowcast it to a `&str`.
let data = data.downcast_ref::<&str>().ok_or_else(|| Error::msg("not a str"))?;
// We should have got the data we created the `externref` with!
assert_eq!(*data, "hello");
sourcepub fn data_mut<'a, T>(
&self,
store: impl Into<StoreContextMut<'a, T>>,
) -> Result<&'a mut (dyn Any + Send + Sync)>where
T: 'a,
pub fn data_mut<'a, T>(
&self,
store: impl Into<StoreContextMut<'a, T>>,
) -> Result<&'a mut (dyn Any + Send + Sync)>where
T: 'a,
Get an exclusive borrow of the underlying data for this ExternRef
.
Returns an error if this externref
GC reference has been unrooted (eg
if you attempt to use a Rooted<ExternRef>
after exiting the scope it
was rooted within). See the documentation for
Rooted<T>
for more details.
§Example
let mut store = Store::<()>::default();
let externref = ExternRef::new::<usize>(&mut store, 0)?;
// Access the `externref`'s host data.
let data = externref.data_mut(&mut store)?;
// Dowcast it to a `usize`.
let data = data.downcast_mut::<usize>().ok_or_else(|| Error::msg("not a usize"))?;
// We initialized to zero.
assert_eq!(*data, 0);
// And we can mutate the value!
*data += 10;
sourcepub unsafe fn from_raw(
store: impl AsContextMut,
raw: u32,
) -> Option<Rooted<ExternRef>>
pub unsafe fn from_raw( store: impl AsContextMut, raw: u32, ) -> Option<Rooted<ExternRef>>
Creates a new strongly-owned ExternRef
from the raw value provided.
This is intended to be used in conjunction with Func::new_unchecked
,
Func::call_unchecked
, and ValRaw
with its externref
field.
This function assumes that raw
is an externref value which is
currently rooted within the Store
.
§Unsafety
This function is particularly unsafe
because raw
not only must be a
valid externref value produced prior by to_raw
but it must also be
correctly rooted within the store. When arguments are provided to a
callback with Func::new_unchecked
, for example, or returned via
Func::call_unchecked
, if a GC is performed within the store then
floating externref values are not rooted and will be GC’d, meaning that
this function will no longer be safe to call with the values cleaned up.
This function must be invoked before possible GC operations can happen
(such as calling wasm).
When in doubt try to not use this. Instead use the safe Rust APIs of
TypedFunc
and friends.
sourcepub unsafe fn to_raw(&self, store: impl AsContextMut) -> Result<u32>
pub unsafe fn to_raw(&self, store: impl AsContextMut) -> Result<u32>
Trait Implementations§
Auto Trait Implementations§
impl Freeze for ExternRef
impl RefUnwindSafe for ExternRef
impl Send for ExternRef
impl Sync for ExternRef
impl Unpin for ExternRef
impl UnwindSafe for ExternRef
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
source§unsafe fn clone_to_uninit(&self, dst: *mut T)
unsafe fn clone_to_uninit(&self, dst: *mut T)
clone_to_uninit
)source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more