WASI - Wasmtime

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# WASIp2

You can also browse this source code online and clone the wasmtime repository to run the example locally.

This example shows how to use the `wasmtime-wasi` crate to define WASI functions within a `Linker` which can then be used to instantiate a WebAssembly component.

## WebAssembly Component Source Code

For this WASI example, this Hello World program is compiled to a WebAssembly component using the WASIp2 API.

`wasi.rs`

    use std::thread::sleep;
    use std::time::{Duration, Instant};

    fn main() {
        println!("Hello, world!");
        let start = Instant::now();
        sleep(Duration::from_millis(100));
        println!("Napped for {:?}", Instant::now().duration_since(start));
    }

> Building instructions:

> 1. Have Rust installed
> 2. Add WASIp2 target if you haven't already: `rustup target add wasm32-wasip2`
> 3. `cargo build --target wasm32-wasip2`

Building this program generates `target/wasm32-wasip2/debug/wasi.wasm`, used below.

### Invoke the WASM component

This example shows adding and configuring the WASI imports to invoke the above WASM component.

`main.rs`

    //! Example of instantiating a wasm module which uses WASI imports.

    /*
    You can execute this example with:
        cmake examples/
        cargo run --example wasip2
    */

    use wasmtime::component::{Component, Linker, ResourceTable};
    use wasmtime::*;
    use wasmtime_wasi::p2::bindings::sync::Command;
    use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};

    pub struct ComponentRunStates {
        // These two are required basically as a standard way to enable the impl of IoView and
        // WasiView.
        // impl of WasiView is required by [`wasmtime_wasi::p2::add_to_linker_sync`]
        pub wasi_ctx: WasiCtx,
        pub resource_table: ResourceTable,
        // You can add other custom host states if needed
    }

    impl WasiView for ComponentRunStates {
        fn ctx(&mut self) -> WasiCtxView {
            WasiCtxView {
                ctx: &mut self.wasi_ctx,
                table: &mut self.resource_table,
            }
        }
    }

    fn main() -> Result<()> {
        // Define the WASI functions globally on the `Config`.
        let engine = Engine::default();
        let mut linker = Linker::new(&engine);
        wasmtime_wasi::p2::add_to_linker_sync(&mut linker)?;

        // Create a WASI context and put it in a Store; all instances in the store
        // share this context. `WasiCtx` provides a number of ways to
        // configure what the target program will have access to.
        let wasi = WasiCtx::builder().inherit_stdio().inherit_args().build();
        let state = ComponentRunStates {
            wasi_ctx: wasi,
            resource_table: ResourceTable::new(),
        };
        let mut store = Store::new(&engine, state);

        // Instantiate our component with the imports we've created, and run it.
        let component = Component::from_file(&engine, "target/wasm32-wasip2/debug/wasi.wasm")?;
        let command = Command::instantiate(&mut store, &component, &linker)?;
        let program_result = command.wasi_cli_run().call_run(&mut store)?;
        if program_result.is_err() {
            std::process::exit(1)
        }

        // Alternatively, instead of using `Command`, just instantiate it as a normal component
        // New states
        let wasi = WasiCtx::builder().inherit_stdio().inherit_args().build();
        let state = ComponentRunStates {
            wasi_ctx: wasi,
            resource_table: ResourceTable::new(),
        };
        let mut store = Store::new(&engine, state);
        // Instantiate it as a normal component
        let instance = linker.instantiate(&mut store, &component)?;
        // Get the index for the exported interface
        let interface_idx = instance
            .get_export_index(&mut store, None, "wasi:cli/run@0.2.0")
            .expect("Cannot get `wasi:cli/run@0.2.0` interface");
        // Get the index for the exported function in the exported interface
        let parent_export_idx = Some(&interface_idx);
        let func_idx = instance
            .get_export_index(&mut store, parent_export_idx, "run")
            .expect("Cannot get `run` function in `wasi:cli/run@0.2.0` interface");
        let func = instance
            .get_func(&mut store, func_idx)
            .expect("Unreachable since we've got func_idx");
        // As the `run` function in `wasi:cli/run@0.2.0` takes no argument and return a WASI result that correspond to a `Result<(), ()>`
        // Reference:
        // * https://github.com/WebAssembly/wasi-cli/blob/main/wit/run.wit
        // * Documentation for [Func::typed](https://docs.rs/wasmtime/latest/wasmtime/component/struct.Func.html#method.typed) and [ComponentNamedList](https://docs.rs/wasmtime/latest/wasmtime/component/trait.ComponentNamedList.html)
        let typed = func.typed::<(), (Result<(), ()>,)>(&store)?;
        let (result,) = typed.call(&mut store, ())?;
        // Required, see documentation of TypedFunc::call
        typed.post_return(&mut store)?;
        result.map_err(|_| anyhow::anyhow!("error"))
    }

### Async example

This async example code shows how to use the wasmtime-wasi crate to execute the same WASIp2 component from the example above. This example requires the `wasmtime` crate `async` feature to be enabled.

This does not require any change to the WASIp2 component, it's just the WASIp2 API host functions which are implemented to be async. See wasmtime async support.

    //! Example of instantiating a wasm module which uses WASI preview1 imports
    //! implemented through the async preview2 WASI implementation.

    /*
    You can execute this example with:
        cmake examples/
        cargo run --example wasip2-async
    */

    use wasmtime::component::{Component, Linker, ResourceTable};
    use wasmtime::*;
    use wasmtime_wasi::p2::bindings::Command;
    use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};

    pub struct ComponentRunStates {
        // These two are required basically as a standard way to enable the impl of IoView and
        // WasiView.
        // impl of WasiView is required by [`wasmtime_wasi::p2::add_to_linker_sync`]
        pub wasi_ctx: WasiCtx,
        pub resource_table: ResourceTable,
        // You can add other custom host states if needed
    }

    impl WasiView for ComponentRunStates {
        fn ctx(&mut self) -> WasiCtxView<'_> {
            WasiCtxView {
                ctx: &mut self.wasi_ctx,
                table: &mut self.resource_table,
            }
        }
    }

    #[tokio::main]
    async fn main() -> Result<()> {
        // Construct the wasm engine with async support enabled.
        let mut config = Config::new();
        config.async_support(true);
        let engine = Engine::new(&config)?;
        let mut linker = Linker::new(&engine);
        wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;

        // Create a WASI context and put it in a Store; all instances in the store
        // share this context. `WasiCtx` provides a number of ways to
        // configure what the target program will have access to.
        let wasi = WasiCtx::builder().inherit_stdio().inherit_args().build();
        let state = ComponentRunStates {
            wasi_ctx: wasi,
            resource_table: ResourceTable::new(),
        };
        let mut store = Store::new(&engine, state);

        // Instantiate our component with the imports we've created, and run it.
        let component = Component::from_file(&engine, "target/wasm32-wasip2/debug/wasi.wasm")?;
        let command = Command::instantiate_async(&mut store, &component, &linker).await?;
        let program_result = command.wasi_cli_run().call_run(&mut store).await?;
        match program_result {
            Ok(()) => Ok(()),
            Err(()) => std::process::exit(1),
        }
    }

You can also browse this source code online and clone the wasmtime repository to run the example locally.

## Beyond Basics

Please see these references:

- The book for understanding the component model of WASIp2.
- Bindgen Examples for implementing WASIp2 hosts and guests.