Search code examples
rustrust-rocket

Why do I get "expected struct file1::A found struct file2::A" error when using a struct in multiple files?


I am trying to share a struct between two files, but I am getting an error.

I have the following folder structure:

src/
  Models/
    Login.rs
  Routes/
    LoginRoute.rs
  Services/
    LoginService.rs
  main.rs

In Login.rs I have:

#[derive(Serialize, Deserialize, Debug)]
pub struct UserLoginResponse { 
    id: i32, 
    username: String, 
    token: String
}

In LoginRoute.rs I have:

#[path = "../Models/Login.rs"]
pub mod Login;

#[path = "../Services/LoginService.rs"]
pub mod LoginService;

#[post("/login", format = "application/json", data = "<user>")]
pub async fn login(user: String) -> Json<Login::UserLoginResponse> {      
     
    if let Ok(sk) = LoginService::callAuthenticate(user).await {       
       return sk
......

In LoginService.rs I have:

#[path = "../Models/Login.rs"]
pub mod Login;

pub async fn callAuthenticate(user: String)->  Result<Json<Login::UserLoginResponse>, Error> {
...
let userLoginResponse :Login::UserLoginResponse = Login::UserLoginResponse::new(1,  "admin".to_string(), api_reponse.return_result); 
    Ok(Json(userLoginResponse))
}

I am getting error in LoginRoute.rs on the return sk line:

expected struct 'LoginRoute::Login::UserLoginResponse', found struct 'LoginService::Login:UserLoginResponse'

Solution

  • Please do not use the #[path = ...] attribute for your typical organization; it should only be used in obscure cases. Each time you do mod something, you are declaring a new module. Even if two modules point to the same file due to #[path = ...], they will be distinct.

    So you have multiple UserLoginResponse structs declared:

    • one at crate::LoginRoute::Login::UserLoginResponse
    • one at crate::LoginService::Login:UserLoginResponse
    • and maybe another if you've also declared Login in main.rs.

    Since they're in distinct modules, the Rust compiler sees them as different types, which is not what you want.

    Just use the idiomatic way of declaring modules. If you want to keep your existing folder structure without intermediate mod.rs files, you can declare them all in main.rs like so:

    mod Models {
        pub mod Login;
    }
    mod Routes {
        pub mod LoginRoute;
    }
    mod Services {
        pub mod LoginService;
    }
    

    And then access them elsewhere via crate::Models::Login and whatnot.

    See:


    You've probably already run into warnings from the compiler trying to encourage a specific style: "module [...] should have a snake case name". Idiomatic file structure would typically look like this:

    src/
      models/
        login.rs
        mod.rs
      routes/
        login_route.rs
        mod.rs
      services/
        login_service.rs
        mod.rs
      main.rs
    

    Where main.rs would have:

    mod models;
    mod routes;
    mod services;
    

    And src/models/mod.rs (for example) would have:

    pub mod login;