Generic structure and lifetime

I have a trait where a reference to the DB is passed as a parameter

#[async_trait]
pub trait CreateCustomer<DB> {
    async fn create(pool: DB, input: &CustomerInput);
}

And this is the way I implement it

pub struct Customer{
    name: String, 
} 

pub struct CustomerRepository;

#[async_trait]
impl<DB> CreateCustomer<DB> for CustomerRepository {
    async fn create(pool: &DB, input: CustomerInput) -> Result<CustomerReponse> {
        let response = sqlx::query!(
            r#"INSERT INTO customer(
                customer_name
            )
            VALUES($1)
            RETURNING id
            "#,
            input.name.to_string()
        )
        .fetch_one(pool)
        .await?;
        
         Ok(response)
    }
}

fn call(){

    let pool = ctx.data::<PgPool>()?;
    
    let customer = Customer{ name: "test".to_string()};
    
    let row = CustomerRepository::create(&pool, customer).await?;
}

But I get the following errors

| #[async_trait]
| ^^^^^^^^^^^^^^ lifetimes do not match method in trait


|         .fetch_one(pool)
|                    ^^^^ the trait `Executor<'_>` is not implemented for `&DB`

I am using sqlx and actix-web

How should I do it?

It may just be a typo, but create() in the trait definition takes pool by value, while the impl takes it by reference... Is that intentional?

You can also use cargo-expand to see what the #[async_trait] attribute expands to. That may provide more context as to why the code fails.

I corrected it but I still have the error

I think it doesn't correctly reference the 'DB'
The compiler suggesting you use DB: Executor <'_>

impl<DB: Executor<'_>> ISuspectRepository<DB> for SuspectRepository 

but still with the same error

How could it be solved?

Also, in your trait definition there is no return type to create. Or did you correct that as well? Maybe you should re-post the code or update your original post to reflect what your code actually looks like.

This is complete code

use sqlx::{query_as,  Executor, PgPool};
use async_trait::async_trait;

#[async_trait]
pub trait CreateCustomer<DB> {
    async fn create(pool: DB, input: &CustomerInput);
}

pub struct Customer{
    name: String, 
} 

pub struct CustomerRepository;

#[async_trait]
impl<DB: Executor<'_>> CreateCustomer<DB> for CustomerRepository {
    async fn create(pool: &DB, input: CustomerInput) -> Result<CustomerReponse> {
        let response = sqlx::query!(
            r#"INSERT INTO customer(
                customer_name
            )
            VALUES($1)
            RETURNING id
            "#,
            input.name.to_string()
        )
        .fetch_one(pool)
        .await?;
        
         Ok(response)
    }
}

fn call(){

    let pool = ctx.data::<PgPool>()?;
    let customer = Customer{ name: "test".to_string()};
    let row = CustomerRepository::create(&pool, customer).await?;
}

I see, so you are still missing the return type and taking DB by value vs reference?

// ^^^^^^ missing: `-> Result<CustomResponse>`
// also, `pool: DB`
// whereas the code below has, `pool: &DB`
// similarly, the types for input differ

The project is big, that's why I don't put all the code.

Without using a trait and making direct reference it works

use sqlx::{query_as,  Executor, PgPool};
use async_trait::async_trait;


pub struct Customer{
    name: String, 
} 

pub struct CustomerRepository;


CustomerRepository {
    async fn create(pool: &PgPool, input: CustomerInput) -> Result<CustomerReponse> {
        let response = sqlx::query!(
            r#"INSERT INTO customer(
                customer_name
            )
            VALUES($1)
            RETURNING id
            "#,
            input.name.to_string()
        )
        .fetch_one(pool)
        .await?;
        
         Ok(response)
    }
}

fn call(){

    let pool = ctx.data::<PgPool>()?;
    let customer = Customer{ name: "test".to_string()};
    let row = CustomerRepository::create(&pool, customer).await?;
}

I was able to create a minimal reproduction on the playground.

use async_trait::async_trait;

// stubbed out types to make things compile
type Result<T> = std::result::Result<T, ()>;
trait Executor<'a>: Send + Sync {}
struct PgPool;
struct CustomerInput;
struct CustomerResponse;

#[async_trait]
pub trait CreateCustomer<DB> {
    async fn create(pool: DB, input: &CustomerInput);
}

pub struct Customer {
    name: String,
}

pub struct CustomerRepository;

#[async_trait]
impl<DB: Executor<'_>> CreateCustomer<DB> for CustomerRepository {
    async fn create(pool: &DB, input: CustomerInput) -> Result<CustomerResponse> {
        todo!()
    }
}

When I try to compile it, these are the errors I get:

   Compiling playground v0.0.1 (/playground)
error[E0637]: `'_` cannot be used here
  --> src/lib.rs:22:19
   |
22 | impl<DB: Executor<'_>> CreateCustomer<DB> for CustomerRepository {
   |                   ^^ `'_` is a reserved lifetime name

error[E0053]: method `create` has an incompatible type for trait
  --> src/lib.rs:23:27
   |
12 |     async fn create(pool: DB, input: &CustomerInput);
   |                           -- type in trait
...
22 | impl<DB: Executor<'_>> CreateCustomer<DB> for CustomerRepository {
   |      -- this type parameter
23 |     async fn create(pool: &DB, input: CustomerInput) -> Result<CustomerResponse> {
   |                           ^^^ expected type parameter `DB`, found `&DB`
   |
   = note: expected fn pointer `fn(DB, &'life0 CustomerInput) -> Pin<Box<(dyn Future<Output = ()> + Send + 'async_trait)>>`
              found fn pointer `fn(&'life0 DB, CustomerInput) -> Pin<Box<(dyn Future<Output = std::result::Result<CustomerResponse, ()>> + Send + 'async_trait)>>`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0053, E0637.
For more information about an error, try `rustc --explain E0053`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

The first error is saying you can't use '_ in the impl<DB: Executor<'_>> CreateCustomer<DB> for ..., instead you should give the lifetime a name (e.g. impl<'a, DB: Executor<'a>>).

The second error says your trait's create() method signature is fn(DB, &CustomerInput) but when you tried to implement it, you wrote a method with the signature fn(&DB, CustomerInput) -> Result<CustomerResponse>.

Note how DB is taken by value in one and reference in the other, CustomerInput is taken by reference in one and value in the other, and one implementation returns Result<CustomerResponse> while the other doesn't return anything.

2 Likes

I cannot reproduce it in the playgroud, because it has external libraries, but I have created a small repo in github to be able to reproduce the problem

src
├── gql_type.rs
├── graphql_schema.rs
├── interface_todo.rs <- define the trait
├── lib.rs
├── main.rs
├── result.rs
├── startup.rs
└── todo.rs <- define the query in de DB // Error

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.