Ask HN: Is this a sane way to expose distributed state in Rust?

Hi HN,

I’m experimenting with a Rust runtime that exposes replicated, consensus-backed state by advertising a *procedural macro*. Client crates invoke the macro and get a typed API; the runtime owns both the protocol and the codegen.

At a high level, the DB cell:

* Handles RPC (Upsert / Get / Remove) * Coordinates macro expansion for client APIs * Stores opaque zero-copy blobs (`rkyv`)

Here’s the core of the DB side:

```rust use cell_sdk::; use cell_model::macro_coordination::; use std::pin::Pin;

#[protein] pub struct Upsert { pub key: u64, pub kind: String, pub blob: Vec<u8>, }

#[protein] pub struct Get { pub key: u64, pub kind: String }

#[protein] pub struct Row { pub blob: Option<Vec<u8>> }

#[service] #[derive(Clone)] struct DbService { state: Arc<RwLock<HashMap<(u64, String), Vec<u8>>>>, }

#[handler] impl DbService { async fn upsert(&self, u: Upsert) -> Result<bool> { self.state.write().await.insert((u.key, u.kind), u.blob); Ok(true) } async fn get(&self, g: Get) -> Result<Row> { let val = self.state.read().await.get(&(g.key, g.kind)).cloned(); Ok(Row { blob: val }) } async fn remove(&self, k: u64, kind: String) -> Result<bool> { Ok(self.state.write().await.remove(&(k, kind)).is_some()) } }

fn expand_table(ctx: &ExpansionContext) -> Pin<Box<dyn Future<Output=Result<String>> + Send + '_>> { Box::pin(async move { let struct_name = &ctx.struct_name; let pk = ctx.fields.first().unwrap().0.clone();

        Ok(format!(r#"
            pub struct {struct_name}Table {{ synapse: ::cell_sdk::Synapse }}
            impl {struct_name}Table {{
                pub async fn connect() -> ::anyhow::Result<Self> {{
                    Ok(Self {{ synapse: ::cell_sdk::Synapse::grow("db").await? }})
                }}
                pub async fn save(&self, row: {struct_name}) -> ::anyhow::Result<bool> {{
                    let bytes = ::cell_sdk::rkyv::to_bytes::<_,1024>(&row)?.into_vec();
                    let req = DbProtocol::Upsert {{
                        key: row.{pk},
                        kind: "{struct_name}".into(),
                        blob: bytes,
                    }};
                    self.synapse.fire(&req).await
                }}
            }}
        "#))
    })
}

#[tokio::main] async fn main() -> Result<()> { let db = DbService { state: Default::default() };

    const macros = vec![MacroInfo {
        name: "table".into(),
        kind: MacroKind::Attribute,
        description: "distributed table".into(),
        dependencies: vec![],
    }];

    Runtime::ignite_with_coordination(
        move |req| db.dispatch(req),
        "db",
        macros,
        expand_table,
    ).await
} ```

consumer

```rust use cell_sdk::;

#[expand("db", "table")] #[derive(Archive, Serialize, Clone, Debug)] pub struct Order { pub order_id: u64, pub user_id: u64, pub amount: u64, }

#[tokio::main] async fn main() -> anyhow::Result<()> { let orders = OrderTable::connect().await?;

    orders.save(Order { order_id: 42, user_id: 7, amount: 1000 }).await?;
    let o = orders.get(42).await?.unwrap();
    println!("loaded {o:?}");

    orders.remove(42).await?;
    Ok(())
} ```

The question: *does letting a distributed service own both its protocol and client-side macro make sense, or is this hiding too much complexity?* And: Would this actually be useful for enterprise? Would you use it?

Thanks — looking for design-level feedback, not users.

1 points | by asdfghjqwertyu 2 days ago

0 comments