Snažím se vytvořit čisté architektury strukturu Rzi s některými structs pomocí vlastností pro závislost inverze.
Můj nápad je v podstatě mít:
- A
User
model s jedno pole. - Úložiště, které je v souladu s úložiště vlastnost/rozhraní drží metoda pro načtení uživatelů z MySQL databáze.
- Případ použití, které závisí na úložiště vlastnost/rozhraní, a obdrží instanci tohoto úložiště na instance s
new
metoda. Jsem také držitelemexecute
metoda pro spuštění úložiště akci. - Řadič, který závisí na případu užití znaku/rozhraní, a obdrží instanci tento případ použití při spouštění s
new
metoda. Je také držitelemexecute
metoda vyvolat případ užití akci.
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>
Mám provádění tohoto, ale mám problém s generics. Základní kód:
Kód:
// 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);
}
Jak můžete vidět, problém je v provedení pro GetUsersUseCase —impl<T: IUserRepository> IGetUsersUseCase for GetUsersUseCase<T>
—. Jako provádění obdrží dvě generické parametry, při stavbě GetUsersUseCase::new(mysql_repo)
Já se zobrazit následující chyba:
cannot infer type for type parameter `T` declared on the struct `GetUsersUseCase`rustc(E0282)
Řešení by bylo, aby se user_repo
na GetUsersUseCase
veřejnost, a místo použití GetUsersUseCase::new(mysql_repo)
instanci je jako obvykle:
[…]
struct GetUsersUseCase<T> {
pub user_repo: T,
}
[…]
let get_users_use_case = GetUsersUseCase {
user_repo: mysql_repo,
};
[…]
To funguje, ale já bych opravdu rád, aby zjistili, jak se postavit structs pomocí veřejné funkce, aniž by odhalil vlastní pole.
Zde je hřiště, takže toto pole veřejnost: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=917cee9d969dccd08c4e27753d04994f
Nějaký nápad bude vítán!
PhantomData
vynutit použití generického param.