In rust, using Arc wrapped struct methods, how do variable references get called

use async_trait::async_trait;
use axum::{
    routing::{get, MethodRouter},
    Router,
};
use std::sync::Arc;

#[tokio::main]
async fn main() {
    let home_rc = Arc::new(Service::new());
    let admin_rc = home_rc.clone();

    App::new()
        .server(Arc::new(GrpcServer::new()))
        .server(Arc::new(
            HttpServer::new()
                .router(RouterData {
                    path: "/",
                    method: get(|| async move { home_rc.home_page().await }),
                })
                .router(RouterData {
                    path: "/admin",
                    method: get(|| async move { admin_rc.home_page().await }),
                }),
        ))
        .run();

    tokio::select! {
        _ = tokio::signal::ctrl_c() => {},
    }
}

struct Service {
    name: String,
}
impl Service {
    fn new() -> Self {
        Self {
            name: "132132".to_string(),
        }
    }
}
impl Service {
    async fn home_page(&mut self) -> String {
        println!("-----123----");
        format!("--Home Page--{}", self.name)
    }

    async fn admin_page(&mut self) -> String {
        println!("-----456----");
        format!("--Admin Page--{}", self.name)
    }
}

struct App {
    servers: Vec<Arc<dyn Server + Send + Sync>>,
}
impl App {
    fn new() -> Self {
        Self {
            servers: Vec::new(),
        }
    }
    fn server(mut self, srv: Arc<dyn Server + Send + Sync>) -> Self {
        self.servers.push(srv);
        self
    }
    fn run(&self) {
        for srv in self.servers.iter() {
            let srv_rc = srv.clone();
            tokio::spawn(async move {
                srv_rc.start().await;
            });
        }
    }
}

struct RouterData {
    path: &'static str,
    method: MethodRouter,
}

#[async_trait]
pub trait Server {
    async fn start(&self);
}

struct HttpServer {
    router: Vec<RouterData>,
}
impl HttpServer {
    fn new() -> Self {
        Self { router: Vec::new() }
    }
    fn router(mut self, r: RouterData) -> Self {
        self.router.push(r);
        self
    }
}

#[async_trait]
impl Server for HttpServer {
    async fn start(&self) {
        println!("-----http server------");

        let mut app = Router::new();
        for r in self.router.iter() {
            app = app.route(r.path, r.method.clone());
        }

        let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
        axum::serve(listener, app).await.unwrap();
    }
}

struct GrpcServer;
impl GrpcServer {
    fn new() -> Self {
        Self
    }
}

#[async_trait]
impl Server for GrpcServer {
    async fn start(&self) {
        println!("-----grpc server------");
    }
}

How to call the home_page and admin_page methods of the Service structure in the router method of the HttpServer structure, home_rc.home_page().await and admin_rc.home_page().await, This call results in an error, as follows:

annot borrow data in an `Arc` as mutable
trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<Service>`

You can't just mutate stuff behind an Arc, for exactly the same reason as you are not allowed to mutate stuff behind a regular shared reference: it's shared, there may be multiple Arcs pointing to the same place at once.

You'll need to use interior mutability. Since you are using Arc, you presumably need concurrency-safety and thread-safety, so a Mutex or an RwLock will solve your immediate problem.

Usually, though, locking in an async context is ill-advised, and you should seek a solution that does not actually force you to write &mut self methods.

1 Like

home_page and admin_page must be asynchronous and mutable references. What's a good way to do that

I already answered that one, use a lock if you must.

Just remember that normal Mutex couldn't be held across await points and there are Tokio-specific Mutex if you need to do that.