Make sure you have the latest Rust version installed and have cargo
in the path. The recommended way to do so is using https://rustup.rs:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup install stable && rustup default stable
rustup target add wasm32-wasi
The easiest way to build Golem templates with Rust is using the compatible version of https://github.com/bytecodealliance/cargo-component. A prerequisite of cargo-component
is protobuf
(at least 15), which can be installed as described on http://google.github.io/proto-lens/installing-protoc.html:
brew install protobuf
Then install the required version of cargo-component
:
cargo install --git https://github.com/bytecodealliance/cargo-component cargo-component --rev e57d1d1405ed2d76f1f3d8647480dea700379ff8 --locked --force
cargo component --version
cargo-component-component 0.1.0 (e57d1d1 2023-08-31 wasi:134dddc)
cargo
build tool does not know how to build WebAssembly components alone. The above installed cargo-component
tool is an extension to it adding a new subset of commands starting with cargo component ...
cargo component build
makes sure the compiler toolchain produces a WebAssembly component model target and that all the necessary bindings are generated.The easiest way to get started once the tooling is installed is to use the golem new
command as described in the Quickstart.
If you prefer to set up your project manually, you first need to create a new template:
cargo component new --reactor golem-test
Modify the interface definition for the template in wit/world.wit
:
package my:template
interface api {
record product-item {
product-id: string,
name: string,
price: float32,
quantity: u32,
}
record order {
order-id: string,
items: list<product-item>,
total: float32,
timestamp: u64,
}
record order-confirmation {
order-id: string,
}
variant checkout-result {
error(string),
success(order-confirmation),
}
initialize-cart: func(user-id: string) -> ()
add-item: func(item: product-item) -> ()
remove-item: func(product-id: string) -> ()
update-item-quantity: func(product-id: string, quantity: u32) -> ()
checkout: func() -> checkout-result
get-cart-contents: func() -> list<product-item>
}
world shopping-cart {
export api
}
The functions exported in the world
in this file will be exposed by Golem.
In the Rust source code you generate and import the exported symbols as:
cargo_component_bindings::generate!();
use bindings::exports::my::template::api::*;
and then implement the Guest
trait from the interface definition:
struct Component;
impl Guest for Component {
fn initialize_cart(user_id: String) {
// ...
}
// ...
}
The struct you implement the trait for must be called Component
!
When running a Golem component you don’t have to care about concurrent access of global state - each worker based on the template runs completely separated. The worker’s state is persisted by Golem so all you have to do for state handling is to store data in a global mutable variable.
In Rust we recommend the following pattern for this:
struct State {
user_id: String,
items: Vec<ProductItem>,
}
static mut STATE: State = State {
user_id: String::new(),
items: vec![],
};
fn with_state<T>(f: impl FnOnce(&mut State) -> T) -> T {
let result = unsafe { f(&mut STATE) };
return result;
}
// ...
fn add_item(item: ProductItem) {
with_state(|state| {
println!(
"Adding item {:?} to the cart of user {}",
item, state.user_id
);
state.items.push(item);
});
}
Rust already support WASI Preview1 so by using its standard libraries to work with IO, file systems, random numbers will automatically work with Golem.
To use any of the new WASI APIs, you need to explicitly put the WASI interface definitions in the wit/deps
folder. Use the WASI interface definitions packaged with golem-cli
to make sure they are compatible with the version implemented by Golem as these interfaces are still not finalized.
The dependencies has to be added to the Cargo.toml
file for cargo-component
, for example:
[package.metadata.component.target.dependencies]
"wasi:poll" = { path = "wit/deps/poll"}
"wasi:io" = { path = "wit/deps/io" }
"wasi:http" = { path = "wit/deps/http" }
This allows using these WIT packages in your main WIT file, making them available from Rust:
import wasi:poll/poll
import wasi:io/streams
import wasi:http/types
import wasi:http/outgoing-handler
Golem implements the WASI-HTTP interface so any library built on that could be used from Golem components to communicate with external services.
At the time of writing the only library available is our fork of reqwest
To use it, just add the following dependency to your component’s Cargo.toml
:
[dependencies]
reqwest = { git = "https://github.com/zivergetech/reqwest", branch = "update-aug-2023", features = ["json"] }
There is no need to manually add the WASI-HTTP interfaces to your wit/deps
folder.
The fork provides the same API as the official reqwest
blocking API, so please use the library’s documentation for more information: https://docs.rs/reqwest/latest/reqwest/
Creating the Golem template is just running:
cargo component build --release
The output is target/wasm32-wasi/release/component_name.wasm
and it is ready to be uploaded to Golem Cloud. (the output file’s name depends on the project!)