#[repr(transparent)]
pub struct Func(_);
Expand description

A WebAssembly function which can be called.

This type can represent either an exported function from a WebAssembly module or a host-defined function which can be used to satisfy an import of a module. Func and can be used to both instantiate an Instance as well as be extracted from an Instance.

A Func “belongs” to the store that it was originally created within. Operations on a Func only work with the store it belongs to, and if another store is passed in by accident then methods will panic.

Func and async

Functions from the perspective of WebAssembly are always synchronous. You might have an async function in Rust, however, which you’d like to make available from WebAssembly. Wasmtime supports asynchronously calling WebAssembly through native stack switching. You can get some more information about asynchronous configs, but from the perspective of Func it’s important to know that whether or not your Store is asynchronous will dictate whether you call functions through Func::call or [Func::call_async] (or the typed wrappers such as TypedFunc::call vs [TypedFunc::call_async]).

To Func::call or to Func::typed().call()

There’s a 2x2 matrix of methods to call Func. Invocations can either be asynchronous or synchronous. They can also be statically typed or not. Whether or not an invocation is asynchronous is indicated via the method being async and call_async being the entry point. Otherwise for statically typed or not your options are:

  • Dynamically typed - if you don’t statically know the signature of the function that you’re calling you’ll be using Func::call or [Func::call_async]. These functions take a variable-length slice of “boxed” arguments in their Val representation. Additionally the results are returned as an owned slice of Val. These methods are not optimized due to the dynamic type checks that must occur, in addition to some dynamic allocations for where to put all the arguments. While this allows you to call all possible wasm function signatures, if you’re looking for a speedier alternative you can also use…

  • Statically typed - if you statically know the type signature of the wasm function you’re calling, then you’ll want to use the Func::typed method to acquire an instance of TypedFunc. This structure is static proof that the underlying wasm function has the ascripted type, and type validation is only done once up-front. The TypedFunc::call and [TypedFunc::call_async] methods are much more efficient than Func::call and [Func::call_async] because the type signature is statically known. This eschews runtime checks as much as possible to get into wasm as fast as possible.

Examples

One way to get a Func is from an Instance after you’ve instantiated it:

let engine = Engine::default();
let module = Module::new(&engine, r#"(module (func (export "foo")))"#)?;
let mut store = Store::new(&engine, ());
let instance = Instance::new(&mut store, &module, &[])?;
let foo = instance.get_func(&mut store, "foo").expect("export wasn't a function");

// Work with `foo` as a `Func` at this point, such as calling it
// dynamically...
match foo.call(&mut store, &[], &mut []) {
    Ok(()) => { /* ... */ }
    Err(trap) => {
        panic!("execution of `foo` resulted in a wasm trap: {}", trap);
    }
}
foo.call(&mut store, &[], &mut [])?;

// ... or we can make a static assertion about its signature and call it.
// Our first call here can fail if the signatures don't match, and then the
// second call can fail if the function traps (like the `match` above).
let foo = foo.typed::<(), (), _>(&store)?;
foo.call(&mut store, ())?;

You can also use the wrap function to create a Func

let mut store = Store::<()>::default();

// Create a custom `Func` which can execute arbitrary code inside of the
// closure.
let add = Func::wrap(&mut store, |a: i32, b: i32| -> i32 { a + b });

// Next we can hook that up to a wasm module which uses it.
let module = Module::new(
    store.engine(),
    r#"
        (module
            (import "" "" (func $add (param i32 i32) (result i32)))
            (func (export "call_add_twice") (result i32)
                i32.const 1
                i32.const 2
                call $add
                i32.const 3
                i32.const 4
                call $add
                i32.add))
    "#,
)?;
let instance = Instance::new(&mut store, &module, &[add.into()])?;
let call_add_twice = instance.get_typed_func::<(), i32, _>(&mut store, "call_add_twice")?;

assert_eq!(call_add_twice.call(&mut store, ())?, 10);

Or you could also create an entirely dynamic Func!

let mut store = Store::<()>::default();

// Here we need to define the type signature of our `Double` function and
// then wrap it up in a `Func`
let double_type = wasmtime::FuncType::new(
    [wasmtime::ValType::I32].iter().cloned(),
    [wasmtime::ValType::I32].iter().cloned(),
);
let double = Func::new(&mut store, double_type, |_, params, results| {
    let mut value = params[0].unwrap_i32();
    value *= 2;
    results[0] = value.into();
    Ok(())
});

let module = Module::new(
    store.engine(),
    r#"
        (module
            (import "" "" (func $double (param i32) (result i32)))
            (func $start
                i32.const 1
                call $double
                drop)
            (start $start))
    "#,
)?;
let instance = Instance::new(&mut store, &module, &[double.into()])?;
// .. work with `instance` if necessary

Implementations

Creates a new Func from the given Rust closure.

This function will create a new Func which, when called, will execute the given Rust closure. Unlike [Func::new] the target function being called is known statically so the type signature can be inferred. Rust types will map to WebAssembly types as follows:

Rust Argument TypeWebAssembly Type
i32i32
u32i32
i64i64
u64i64
f32f32
f64f64
(not supported)v128
Option<Func>funcref
Option<ExternRef>externref

Any of the Rust types can be returned from the closure as well, in addition to some extra types

Rust Return TypeWebAssembly Return TypeMeaning
()nothingno return value
TTa single return value
(T1, T2, ...)T1 T2 ...multiple returns

Note that all return types can also be wrapped in Result<_, Trap> to indicate that the host function can generate a trap as well as possibly returning a value.

Finally you can also optionally take Caller as the first argument of your closure. If inserted then you’re able to inspect the caller’s state, for example the Memory it has exported so you can read what pointers point to.

Note that when using this API, the intention is to create as thin of a layer as possible for when WebAssembly calls the function provided. With sufficient inlining and optimization the WebAssembly will call straight into func provided, with no extra fluff entailed.

Why Send + Sync + 'static?

All host functions defined in a Store (including those from [Func::new] and other constructors) require that the func provided is Send + Sync + 'static. Additionally host functions always are Fn as opposed to FnMut or FnOnce. This can at-a-glance feel restrictive since the closure cannot close over as many types as before. The reason for this, though, is to ensure that Store<T> can implement both the Send and Sync traits.

Fear not, however, because this isn’t as restrictive as it seems! Host functions are provided a Caller<'_, T> argument which allows access to the host-defined data within the Store. The T type is not required to be any of Send, Sync, or 'static! This means that you can store whatever you’d like in T and have it accessible by all host functions. Additionally mutable access to T is allowed through Caller::data_mut.

Most host-defined Func values provide closures that end up not actually closing over any values. These zero-sized types will use the context from Caller for host-defined information.

Examples

First up we can see how simple wasm imports can be implemented, such as a function that adds its two arguments and returns the result.

let add = Func::wrap(&mut store, |a: i32, b: i32| a + b);
let module = Module::new(
    store.engine(),
    r#"
        (module
            (import "" "" (func $add (param i32 i32) (result i32)))
            (func (export "foo") (param i32 i32) (result i32)
                local.get 0
                local.get 1
                call $add))
    "#,
)?;
let instance = Instance::new(&mut store, &module, &[add.into()])?;
let foo = instance.get_typed_func::<(i32, i32), i32, _>(&mut store, "foo")?;
assert_eq!(foo.call(&mut store, (1, 2))?, 3);

We can also do the same thing, but generate a trap if the addition overflows:

let add = Func::wrap(&mut store, |a: i32, b: i32| {
    match a.checked_add(b) {
        Some(i) => Ok(i),
        None => Err(Trap::new("overflow")),
    }
});
let module = Module::new(
    store.engine(),
    r#"
        (module
            (import "" "" (func $add (param i32 i32) (result i32)))
            (func (export "foo") (param i32 i32) (result i32)
                local.get 0
                local.get 1
                call $add))
    "#,
)?;
let instance = Instance::new(&mut store, &module, &[add.into()])?;
let foo = instance.get_typed_func::<(i32, i32), i32, _>(&mut store, "foo")?;
assert_eq!(foo.call(&mut store, (1, 2))?, 3);
assert!(foo.call(&mut store, (i32::max_value(), 1)).is_err());

And don’t forget all the wasm types are supported!

let debug = Func::wrap(&mut store, |a: i32, b: u32, c: f32, d: i64, e: u64, f: f64| {

    println!("a={}", a);
    println!("b={}", b);
    println!("c={}", c);
    println!("d={}", d);
    println!("e={}", e);
    println!("f={}", f);
});
let module = Module::new(
    store.engine(),
    r#"
        (module
            (import "" "" (func $debug (param i32 i32 f32 i64 i64 f64)))
            (func (export "foo")
                i32.const -1
                i32.const 1
                f32.const 2
                i64.const -3
                i64.const 3
                f64.const 4
                call $debug))
    "#,
)?;
let instance = Instance::new(&mut store, &module, &[debug.into()])?;
let foo = instance.get_typed_func::<(), (), _>(&mut store, "foo")?;
foo.call(&mut store, ())?;

Finally if you want to get really fancy you can also implement imports that read/write wasm module’s memory

use std::str;

let log_str = Func::wrap(&mut store, |mut caller: Caller<'_, ()>, ptr: i32, len: i32| {
    let mem = match caller.get_export("memory") {
        Some(Extern::Memory(mem)) => mem,
        _ => return Err(Trap::new("failed to find host memory")),
    };
    let data = mem.data(&caller)
        .get(ptr as u32 as usize..)
        .and_then(|arr| arr.get(..len as u32 as usize));
    let string = match data {
        Some(data) => match str::from_utf8(data) {
            Ok(s) => s,
            Err(_) => return Err(Trap::new("invalid utf-8")),
        },
        None => return Err(Trap::new("pointer/length out of bounds")),
    };
    assert_eq!(string, "Hello, world!");
    println!("{}", string);
    Ok(())
});
let module = Module::new(
    store.engine(),
    r#"
        (module
            (import "" "" (func $log_str (param i32 i32)))
            (func (export "foo")
                i32.const 4   ;; ptr
                i32.const 13  ;; len
                call $log_str)
            (memory (export "memory") 1)
            (data (i32.const 4) "Hello, world!"))
    "#,
)?;
let instance = Instance::new(&mut store, &module, &[log_str.into()])?;
let foo = instance.get_typed_func::<(), (), _>(&mut store, "foo")?;
foo.call(&mut store, ())?;

Returns the underlying wasm type that this Func has.

Panics

Panics if store does not own this function.

Invokes this function with the params given and writes returned values to results.

The params here must match the type signature of this Func, or a trap will occur. If a trap occurs while executing this function, then a trap will also be returned. Additionally results must have the same length as the number of results for this function.

Panics

This function will panic if called on a function belonging to an async store. Asynchronous stores must always use call_async. initiates a panic. Also panics if store does not own this function.

Invokes this function in an “unchecked” fashion, reading parameters and writing results to params_and_returns.

This function is the same as Func::call except that the arguments and results both use a different representation. If possible it’s recommended to use Func::call if safety isn’t necessary or to use Func::typed in conjunction with TypedFunc::call since that’s both safer and faster than this method of invoking a function.

Note that if this function takes externref arguments then it will not automatically GC unlike the Func::call and TypedFunc::call functions. This means that if this function is invoked many times with new ExternRef values and no other GC happens via any other means then no values will get collected.

Unsafety

This function is unsafe because the params_and_returns argument is not validated at all. It must uphold invariants such as:

  • It’s a valid pointer to an array
  • It has enough space to store all parameters
  • It has enough space to store all results (not at the same time as parameters)
  • Parameters are initially written to the array and have the correct types and such.
  • Reference types like externref and funcref are valid at the time of this call and for the store specified.

These invariants are all upheld for you with Func::call and TypedFunc::call.

Converts the raw representation of a funcref into an Option<Func>

This is intended to be used in conjunction with [Func::new_unchecked], Func::call_unchecked, and ValRaw with its funcref field.

Unsafety

This function is not safe because raw is not validated at all. The caller must guarantee that raw is owned by the store provided and is valid within the store.

Extracts the raw value of this Func, which is owned by store.

This function returns a value that’s suitable for writing into the funcref field of the ValRaw structure.

Unsafety

The returned value is only valid for as long as the store is alive and this function is properly rooted within it. Additionally this function should not be liberally used since it’s a very low-level knob.

Attempts to extract a typed object from this Func through which the function can be called.

This function serves as an alternative to Func::call and [Func::call_async]. This method performs a static type check (using the Params and Results type parameters on the underlying wasm function. If the type check passes then a TypedFunc object is returned, otherwise an error is returned describing the typecheck failure.

The purpose of this relative to Func::call is that it’s much more efficient when used to invoke WebAssembly functions. With the types statically known far less setup/teardown is required when invoking WebAssembly. If speed is desired then this function is recommended to be used instead of Func::call (which is more general, hence its slowdown).

The Params type parameter is used to describe the parameters of the WebAssembly function. This can either be a single type (like i32), or a tuple of types representing the list of parameters (like (i32, f32, f64)). Additionally you can use () to represent that the function has no parameters.

The Results type parameter is used to describe the results of the function. This behaves the same way as Params, but just for the results of the function.

The S type parameter represents the method of passing in the store context, and can typically be specified as simply _ when calling this function.

Translation between Rust types and WebAssembly types looks like:

WebAssemblyRust
i32i32 or u32
i64i64 or u64
f32f32
f64f64
externrefOption<ExternRef>
funcrefOption<Func>
v128not supported

(note that this mapping is the same as that of Func::wrap).

Note that once the TypedFunc return value is acquired you’ll use either TypedFunc::call or [TypedFunc::call_async] as necessary to actually invoke the function. This method does not invoke any WebAssembly code, it simply performs a typecheck before returning the TypedFunc value.

This method also has a convenience wrapper as Instance::get_typed_func to directly get a typed function value from an Instance.

Errors

This function will return an error if Params or Results does not match the native type of this WebAssembly function.

Panics

This method will panic if store does not own this function.

Examples

An end-to-end example of calling a function which takes no parameters and has no results:

let engine = Engine::default();
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, r#"(module (func (export "foo")))"#)?;
let instance = Instance::new(&mut store, &module, &[])?;
let foo = instance.get_func(&mut store, "foo").expect("export wasn't a function");

// Note that this call can fail due to the typecheck not passing, but
// in our case we statically know the module so we know this should
// pass.
let typed = foo.typed::<(), (), _>(&store)?;

// Note that this can fail if the wasm traps at runtime.
typed.call(&mut store, ())?;

You can also pass in multiple parameters and get a result back

let typed = add.typed::<(i32, i64), f32, _>(&store)?;
assert_eq!(typed.call(&mut store, (1, 2))?, 3.0);

and similarly if a function has multiple results you can bind that too

let typed = add_with_overflow.typed::<(u32, u32), (u32, i32), _>(&store)?;
let (result, overflow) = typed.call(&mut store, (u32::max_value(), 2))?;
assert_eq!(result, 1);
assert_eq!(overflow, 1);

Trait Implementations

Returns a copy of the value. Read more

Performs copy-assignment from source. Read more

Formats the value using the given formatter. Read more

Performs the conversion.

Performs the conversion.

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Performs the conversion.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more

Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Performs the conversion.

The resulting type after obtaining ownership.

Creates owned data from borrowed data, usually by cloning. Read more

🔬 This is a nightly-only experimental API. (toowned_clone_into)

Uses borrowed data to replace owned data, usually by cloning. Read more

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more