Topics:

Rebuilding a Rocket Server with Actix via @mysteriouspants

Repo: https://github.com/idevgames/idevgames.com

Farewell Rocket, Hello Actix

Chris is trying Actix because Rocket has been rather irritating him. It boils down to some of the boilerplate that Rocket has when you start to do nontrivial things. For example, in Rocket to return a result of a handler, you do something that can give an Outcome. This can be a Template or a Redirect - but critically they're two different types. So if you want "a redirect to the login page, or the page template itself, or an error page if the database has gone on holiday" you end up with some abomination of a return type like Result<Template, Result<Redirect, MyError>> and then your handler code gets really messy converting into that.

The thing that drew Chris to want to try Actix is that its Actix-Web return type is a plain HttpResponse which has some builder methods to construct what you actually want - e.g. HttpResponse::Ok().content_type("text/html").body("<!doctype html>...")

While it looks wordier it makes that initial decision between Template and Redirect much easier, since the redirect case turns into HttpResponse::Unauthorized().header("Location", "/login") or some such.

This is kind of counter to Chris's first reaction, that the more granular types in Rocket were better, where after some thunking around and writing all too much boilerplate it seems that the more general types in Actix look to be a better fit for purpose.

Routing

One difference between Rocket and Actix is how they handle routing. With Rocket, routing happens on your handlers. With Actix, routing happens when you actually connect controllers.

Error Handling

Chris used thiserror to convert the different types of errors that could occur in the wild into a HandlerError which can produce an HTTP error code.

Magical Destructuring of a Web Path

One interesting point was how Actix was able to destructure a snippet_id from a parameterized tuple of web::Path() in the controller logic.

// GET /snippets/{taxonomy}/{snippet_id}/edit form for editing an existing snippet
pub async fn edit(
    ctxt: web::Data<ApplicationContext>,
    session: Session,
    web::Path((taxonomy, snippet_id)): web::Path<(String, i32)>,
) -> Result<HttpResponse, super::HandlerError> {
    ...
}

We verified that it is not type-checking as the following also works without compiler errors:

// GET /snippets/{taxonomy}/{snippet_id}/edit form for editing an existing snippet
pub async fn edit(
    ctxt: web::Data<ApplicationContext>,
    session: Session,
    web::Path(taxonomy): web::Path<String>,
) -> Result<HttpResponse, super::HandlerError> {
    ...
}

Using the Tera Templating Engine

Previously, Chris was using Zola for a templating engine, but he didn't like that it required a code change every time he wanted to update the site. He changed to Tera which is inspired by Jinja2 and the Django templating engine from Python. He also likes horrorshow, which uses a macro-based approach.

Dark Forest Tweeter via @jacobrosenthal

Repo: https://github.com/jacobrosenthal/sophon_tweets

Jacob shared more details of his Sophon Tweeter project.

Simple Polling of Events

It uses the same pattern for polling futures using futures_micro that he used in his Rust SHTCx / SHTWx Driver project.

    futures_micro::or!(
        ctrl_c,
        collect_from_graph(wrapped_state.clone()), //COLLECT_DELAY
        collect_from_node(wrapped_state.clone()),  //COLLECT_DELAY
        tweets(wrapped_state.clone()),             //STAGGER_DELAY
        tweet_counts(),                            //COUNTS_DELAY
    )
    .await
    .unwrap();

Shared State Across Threads

It loads / saves state to a state file and the threads share the state using a Mutex:

async fn tweets(wrapped_state: Arc<Mutex<SophonShare>>) -> Result<(), SophonError> {
    loop {
        // scope for mutex release
        {
            let mut share = wrapped_state.lock().await;
            ...
        }
    }
}

wrapped_state.lock() returns a MutexGuard which unlocks the lock when it is dropped (falls out of scope). So there is no issue with forgetting to unlock the Mutex.

Pulling Data from the Dark Forest Graph

There is a GraphQL query for pulling data from the Dark Forest Graph and serializing it into purpose-built structs. This all happens in the graph.rs file.

The Repo for creating the graph is written in TypeScript: https://github.com/jacobrosenthal/darkforest-graph

Thinking about Mobile Development with Rust via @DanielPBank

Daniel is on a quest to build a mobile app. Naturally he would like to use Rust, but is unsure how to go about it. There were two possible options discussed.

  • Option 1: Using cargo-mobile, the entire app could be built in Rust (without even needing to touch Android Studio or XCode!). You simply run cargo android run and it will build and run on a connected Android device. Cargo-mobile projects are based off of template packs which, at the time of writing, include packs for bevy, wgpu, and winit. Jacob likes this approach because you don't have to touch stinky IDE's and can write entirely in Rust. Daniel was scared by the limited documentation, lack of tutorials, and newness of the project. If you were building a Bevy game, this would be a cool project to check out though.

Under the hood, cargo-mobile is using android-ndk-rs, a wrapper for the Android NDK.

Random aside about Bevy: there is a really cool demo of running the MNIST classifier with Bevy and Tract

  • Option 2: We can write the bulk of the mobile apps in their respective language (Kotlin or Swift) and only write cross-platform functionality with Rust. There is a good tutorial that outlines the process for building a shared Rust library that interfaces using Foreign Function Interface.

Rant about the State of Rust Web Frameworks via @ChristopherSebastian

Chris rants about the difficulty in working with the various Rust Web Frameworks. There are too many dependencies, too many layers of abstractions, and it creates an unpredictable amount of work. The types are more important than the memory or performance. The ecosystem still feels immature.

Of all the frameworks, he likes tide the best.

Crates You Should Know

  • cargo-mobile: Coming soon, a way to build mobile apps with Rust
  • trunk: Build WASM Web Apps without the intermediary NPM Modules
  • tera: A template engine inspired by Jinja2 and the Django template language
  • horrorshow: A templating library written in Rust macros