This is just an example of an (still incomplete) real-world project written in Rust using a clean architecture: https://github.com/frederikhors/rust-clean-architecture-with-db-transactions.
Goals
My intent is to have an app build in 4 layers:
entities
:- some call this layer "domain", not important for now, just the minimum
services
:- some call this layer "use cases", this is where business logic lives (just CRUD methods for now)
repositories
:- some call this layer "adapters", this is where concrete implementation of DB/cache/mail drivers lives
ports
:- some call this layer "controllers or presenters", still not present and not important for now, I'm using
main.rs
for this
- some call this layer "controllers or presenters", still not present and not important for now, I'm using
Reproduction
https://codesandbox.io/p/github/frederikhors/rust-clean-architecture-with-db-transactions/main
The issue
If you open the main.rs
file you can see the issue:
// This obviously works if alone:
// let db_repo = Arc::new(repositories::in_memory::Repo::new());
// This obviously works if alone:
// let pg_pool = Arc::new(sqlx::PgPool::connect("postgres://postgres:postgres@localhost:5432/postgres").await.unwrap());
// let db_repo = Arc::new(repositories::postgres::Repo::new(pg_pool));
// This doesn't work instead:
let db_repo = if use_postgres {
let pg_pool = Arc::new(sqlx::PgPool::connect("postgres://postgres:postgres@localhost:5432/postgres").await.unwrap());
Arc::new(repositories::postgres::Repo::new(pg_pool))
} else {
Arc::new(repositories::in_memory::Repo::new())
};
My intent here is to change repository in use based on a variable, but Rust doesn't like it, this is the error:
Expand the errorerror[E0308]: `if` and `else` have incompatible types
--> src\main.rs:37:9
|
28 | let db_repo = if use_postgres {
| ___________________-
29 | | let pg_pool = Arc::new(
30 | | sqlx::PgPool::connect("postgres://postgres:postgres@localhost:5432/postgres")
31 | | .await
... |
35 | | Arc::new(repositories::postgres::Repo::new(pg_pool))
| | ---------------------------------------------------- expected because of this
36 | | } else {
37 | | Arc::new(repositories::in_memory::Repo::new())
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `repositories::postgres::Repo`, found struct `in_memory::Repo`
38 | | };
| |_____- `if` and `else` have incompatible types
|
= note: struct `in_memory::Repo` and struct `repositories::postgres::Repo` have similar names, but are actually distinct types
note: struct `in_memory::Repo` is defined in module `crate::repositories::in_memory` of the current crate
--> src\repositories\in_memory\mod.rs:6:1
|
6 | pub struct Repo {
| ^^^^^^^^^^^^^^^
note: struct `repositories::postgres::Repo` is defined in module `crate::repositories::postgres` of the current crate
--> src\repositories\postgres\mod.rs:6:1
|
6 | pub struct Repo {
| ^^^^^^^^^^^^^^^
How can I fix this?
Like always with different types you have essentialy 2 options, static or dynamic dispatch.
For dynamic dispatch both
Repo
types have to implement a common trait that encompasses all necessary functionality and is object safe:or for static dispatch create an
enum
and everytime you have to deal with it you pattern match: