Patterns for library authors


Let's take a look at an implementation of a library that incrementally counts the number of conns that pass through it and attaches the number to each conn. It would be unsafe to store a u64 directly in the state set, because other libraries might be doing so, so we wrap it with a private newtype called ConnNumber. Since this isn't accessible outside of our library, we can be sure that our handler is the only place that sets it. We provide a ConnExt trait in order to provide access to this data.

mod conn_counter {
    use std::sync::{
        atomic::{AtomicU64, Ordering},
    use trillium::{async_trait, Conn, Handler};

    struct ConnNumber(u64);

    pub struct ConnCounterHandler(Arc<AtomicU64>);

    impl ConnCounterHandler {
        pub fn new() -> Self {

    impl Handler for ConnCounterHandler {
        async fn run(&self, conn: Conn) -> Conn {
            let number = self.0.fetch_add(1, Ordering::SeqCst);

    pub trait ConnCounterConnExt {
        fn conn_number(&self) -> u64;

    impl ConnCounterConnExt for Conn {
        fn conn_number(&self) -> u64 {
                .expect("conn_number must be called after the handler")

And usage of the library looks like this:

use conn_counter::{ConnCounterConnExt, ConnCounterHandler};
use trillium::{Conn, Handler};

fn handler() -> impl Handler {
    (ConnCounterHandler::new(), |conn: Conn| async move {
        let conn_number = conn.conn_number();
        conn.ok(format!("conn number was {conn_number}"))

fn main() {

mod test {
    use trillium_testing::prelude::*;

    fn test_conn_counter() {
        let handler = super::handler();
        assert_ok!(get("/").on(&handler), "conn number was 0");
        assert_ok!(get("/").on(&handler), "conn number was 1");
        assert_ok!(get("/").on(&handler), "conn number was 2");
        assert_ok!(get("/").on(&handler), "conn number was 3");