I am trying to create a clean architecture structure in Rust with some structs using traits for dependency inversion.
My idea is basically to have:
- A
User
model with one field only. - A repository that complies with a repository trait/interface holding a method to retrieve users from a MySQL database.
- A use case that depends on the repository trait/interface, and receives an instance of this repository on instantiation with a
new
method. I also holds anexecute
method to trigger the repository action. - A controller that depends on the use case trait/interface, and receives an instance of this use case when instantiating it with a
new
method. It also holds anexecute
method to trigger the use case action.
User:
+ id
UserRepository complies with IUserRepository:
- get_all_users: () -> Vec<Users>
GetUsersUseCase complies with IGetUsersUseCase:
+ user_repository: IUserRepository
- new: (user_repository: IUserRepository) -> GetUsersUseCase
- execute: () -> Vec<Users>
GetUsersController:
+ get_users_use_case: IGetUsersUseCase
- new: (user_use_case: IGetUsersUseCase) -> GetUsersController
- execute: () -> Vec<Users>
I have an implementation of this, but I have a problem with the generics. The basic code:
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ca1f4f9d6cfe4df2864d7e574966ad6b.
Code:
// MODEL
#[derive(Debug)]
struct User {
id: i8,
}
// REPOSITORY for DB
trait IUserRepository {
fn get_users(&self) -> Vec<User>;
}
struct MySQLUserRepository;
impl IUserRepository for MySQLUserRepository {
fn get_users(&self) -> Vec<User> {
let mock_user1 = User { id: 1 };
let mock_user2 = User { id: 2 };
let mock_users = vec![mock_user1, mock_user2];
mock_users
}
}
// USE CASE
trait IGetUsersUseCase {
fn new<T: IUserRepository>(repository: T) -> GetUsersUseCase<T>;
fn execute(&self) -> Vec<User>;
}
struct GetUsersUseCase<T> {
user_repo: T,
}
impl<T: IUserRepository> IGetUsersUseCase for GetUsersUseCase<T> {
fn new<K: IUserRepository>(user_repo: K) -> GetUsersUseCase<K> {
GetUsersUseCase { user_repo }
}
fn execute(&self) -> Vec<User> {
let users = self.user_repo.get_users();
users
}
}
// CONTROLLER for HTTP requests
struct GetUsersController<T> {
get_users_use_case: T,
}
impl<T: IGetUsersUseCase> GetUsersController<T> {
fn new(get_users_use_case: T) -> GetUsersController<T> {
GetUsersController { get_users_use_case }
}
fn execute(&self) -> Vec<User> {
let users = self.get_users_use_case.execute();
users
}
}
fn main() {
// Lets imagine we are handling an HTTP request
let mysql_repo = MySQLUserRepository {};
// Error here: cannot infer type for type parameter `T` declared on the struct `GetUsersUseCase`
let get_users_use_case = GetUsersUseCase::new(mysql_repo);
let get_users_controller = GetUsersController::new(get_users_use_case);
let users = get_users_controller.execute();
println!("{:?}", users);
}
As you may see, the issue is at the implementation for GetUsersUseCase —impl<T: IUserRepository> IGetUsersUseCase for GetUsersUseCase<T>
—. As the implementation receives two generic parameters, when constructing GetUsersUseCase::new(mysql_repo)
I receive the following error:
cannot infer type for type parameter `T` declared on the struct `GetUsersUseCase`rustc(E0282)
A workaround would be to make user_repo
at GetUsersUseCase
public, and instead of using GetUsersUseCase::new(mysql_repo)
instantiate it as usual:
[…]
struct GetUsersUseCase<T> {
pub user_repo: T,
}
[…]
let get_users_use_case = GetUsersUseCase {
user_repo: mysql_repo,
};
[…]
This works, but I really would like to find out how to construct structs using a public function without exposing private fields.
Here is the playground making this field public: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=917cee9d969dccd08c4e27753d04994f
Any idea will be welcome!