< 返回版块

‘static 发表于 2025-08-16 21:46

Tags:sqlx,生命周期

我想封装一个函数,把具体的数据库类型(如Sqlite,Any)替换为泛型后就会报错生命周期不满足,求教怎么解决?


 use futures_core::future::BoxFuture;
    use sqlx::{Any, Database, Executor, IntoArguments, Sqlite, query_with};

    struct TestQuery<'a, DB: Database> {
        sql: String,
        arg: DB::Arguments<'a>,
    }
    //正常
    impl<'a> TestQuery<'a, Sqlite> {
        fn execute<'e, E>(
            self,
            e: E,
        ) -> BoxFuture<'e, Result<<Sqlite as Database>::QueryResult, sqlx_core::Error>>
        where
            'a: 'e,
            E: Executor<'e, Database = Sqlite> + 'e,
        {
            Box::pin(async move {
                let TestQuery { sql, arg } = self;
                query_with(&sql, arg).execute(e).await
            })
        }
    }
    //正常
    impl<'a> TestQuery<'a, Any> {
        fn execute<'e, E>(
            self,
            e: E,
        ) -> BoxFuture<'e, Result<<Any as Database>::QueryResult, sqlx_core::Error>>
        where
            'a: 'e,
            E: Executor<'e, Database = Any> + 'e,
        {
            Box::pin(async move {
                let TestQuery { sql, arg } = self;
                query_with(&sql, arg).execute(e).await
            })
        }
    }
    //把具体的数据库类型修改为泛型后就不行
    impl<'a, DB: Database> TestQuery<'a, DB> {
        fn execute1<'e, E>(self, e: E) -> BoxFuture<'e, Result<DB::QueryResult, sqlx_core::Error>>
        where
            'a: 'e,
            E: Executor<'e, Database = DB> + 'e,
            DB::Arguments<'a>: IntoArguments<'e, DB>,
        {
            //报错: lifetime may not live long enough 。 coercion requires that `'a` must outlive `'static`
            Box::pin(async move {
                let TestQuery { sql, arg } = self;
                query_with(&sql, arg).execute(e).await
            })
        }
    }
}


评论区

写评论
苦瓜小仔 2025-08-19 09:42

那你可以把 sql 字段变成引用

struct TestQuery<'a, DB: Database> {
    sql: &'a str,
    arg: DB::Arguments<'a>,
}

impl<'a, DB: Database> TestQuery<'a, DB> {
    async fn execute1<'e, E>(self, e: E) -> Result<DB::QueryResult, sqlx_core::Error>
    where
        E: Executor<'e, Database = DB>,
        DB::Arguments<'a>: 'a + IntoArguments<'a, DB>,

gist

sql: String 是不行的,因为 &sql 在 execute1 函数内创建,需要一种方式表达 DB::Arguments<'a> 中的 'a 与 &'q sql 中的 'q 具有 'q: 'a 关系。这无法被 Rust 的 trait bound 表达。

作者 ‘static 2025-08-17 15:32

谢谢大佬!execute1的可以编译过,但是在调用时候,除了pgArguments,mysqlArguments这种不带生命周期的数据库参数,其他像 AnyArguments<'q> 这种带生命周期的就没法绑定参数和调用

    #[tokio::test]
    async fn test_execute() {
        use sqlx::Arguments;
        // Your test code here let id = "9999_i64".to_string();
        let id = "9999_i64".to_string();
        let sql = "delete from t_user where user_id=$1".to_string();
        let mut args = AnyArguments::default();

        let pool = AnyPool::connect("postgres://postgres:postgres@localhost/my_web")
            .await
            .unwrap();
        //`id` does not live long enough
        args.add(&id);
        //implementation of `sqlx::IntoArguments` is not general enough......
        let query: TestQuery<'_, Any> = TestQuery { sql, arg: args };
        let res = query.execute1(&pool).await.unwrap();
        println!("{:?}", res.rows_affected());
    }

--
👇
苦瓜小仔: 你可以这样写(没有 BoxFuture):

impl<'a, DB: Database> TestQuery<'a, DB> {
    async fn execute1<'c, E>(self, e: E) -> Result<DB::QueryResult, sqlx_core::Error>
    where
        E: Executor<'c, Database = DB>,
        for<'q> DB::Arguments<'a>: 'q + IntoArguments<'q, DB>,
    {
        let TestQuery { sql, arg } = self;
        query_with(&sql, arg).execute(e).await
    }
}

gist

苦瓜小仔 2025-08-17 08:55

你可以这样写(没有 BoxFuture):

impl<'a, DB: Database> TestQuery<'a, DB> {
    async fn execute1<'c, E>(self, e: E) -> Result<DB::QueryResult, sqlx_core::Error>
    where
        E: Executor<'c, Database = DB>,
        for<'q> DB::Arguments<'a>: 'q + IntoArguments<'q, DB>,
    {
        let TestQuery { sql, arg } = self;
        query_with(&sql, arg).execute(e).await
    }
}

gist

1 共 3 条评论, 1 页