Skip to main content
Sign In
Quickstart

Rust Quickstart (Preview)

Build a Rivet Actor in Rust

Rust support is in preview. The supported public Rust API is rivetkit and rivetkit-client; lower-level crates are internal implementation details and do not carry a stability guarantee.

Steps

Add Rivet

Add the rivetkit crate:

cargo add rivetkit@2.3.0-rc.12 anyhow async-trait
cargo add serde --features derive
cargo add tokio --features full

Create An Actor And Serve

An actor is a type that implements Actor, plus one Handles<M> implementation for each action. Persisted state lives in type State; ephemeral runtime state is just fields on your actor struct.

use std::{future::Future, pin::Pin, sync::Arc};

use async_trait::async_trait;
use rivetkit::prelude::*;
use serde::{Deserialize, Serialize};

type BoxFuture<T> = Pin<Box<dyn Future<Output = Result<T>> + Send>>;

struct Counter;

#[derive(Default, Serialize, Deserialize)]
struct CounterState {
	count: i64,
}

#[derive(Serialize, Deserialize)]
struct Increment {
	amount: i64,
}

impl Action for Increment {
	type Output = i64;

	const NAME: &'static str = "increment";
}

#[derive(Serialize, Deserialize)]
struct NewCount {
	count: i64,
}

impl Event for NewCount {
	const NAME: &'static str = "newCount";
}

#[async_trait]
impl Actor for Counter {
	type State = CounterState;
	type Input = ();
	type Actions = (Increment,);
	type Events = (NewCount,);
	type Queue = ();
	type ConnParams = ();
	type ConnState = ();
	type Action = action::Raw;

	async fn create_state(_ctx: &Ctx<Self>, _input: Self::Input) -> Result<Self::State> {
		Ok(CounterState::default())
	}

	async fn create(_ctx: &Ctx<Self>) -> Result<Self> {
		Ok(Self)
	}
}

impl Handles<Increment> for Counter {
	type Future = BoxFuture<i64>;

	fn handle(self: Arc<Self>, ctx: Ctx<Self>, action: Increment) -> Self::Future {
		Box::pin(async move {
			let count = {
				let mut state = ctx.state_mut();
				state.count += action.amount;
				state.count
			};
			ctx.emit(NewCount { count })?;
			Ok(count)
		})
	}
}

#[tokio::main]
async fn main() -> Result<()> {
	let mut registry = Registry::new();
	registry.register_actor::<Counter>("counter");
	registry.start().await
}

Run The Server

The Rust runtime connects to the Rivet Engine. Build the engine binary once, then start your server. RIVET_ENGINE_BINARY_PATH tells the runtime where to find the engine; it spawns or reuses a local engine at http://localhost:6420.

cargo build -p rivet-engine
RIVET_ENGINE_BINARY_PATH=./target/debug/rivet-engine cargo run

Your server now connects to the Rivet Engine on http://localhost:6420. Clients connect directly to the engine on this port.

Connect To The Rivet Actor

This code can run either in your frontend or within your backend:

Deploy

By default, Rivet stores actor state on the local file system.

To scale Rivet in production, follow a guide to deploy to your hosting provider of choice: