2

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'
kmdreko
  • 42,554
  • 6
  • 57
  • 106
Gonzalo
  • 45
  • 8

1 Answers1

7

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;
kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • hi thanks, for the answer, I am still new in Rust. I understand the error now, trying to implement answer, I have a question, is there a benefit on using the mod.rs with time those folder will have several files, I have also fixed naming convention to follow snake case. thanks – Gonzalo Aug 26 '21 at 01:34
  • @Gonzalo *"is there a benefit on using the mod.rs"* - its mostly convention. You can do it the first way I mentioned, but it can become unwieldy as your module structure gets deeper. And its often handy to have these intermediate files to put shared logic within a group of files. – kmdreko Aug 26 '21 at 01:41
  • It also makes it easier to reuse a module in another project (or to extract it to its own crate): just copy the folder and add a single `mod` declaration to your `main.rs`/`lib.rs`. – Jmb Aug 26 '21 at 07:00