You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SQLx解码泛型类型时的生命周期处理问题

问题:SQLx泛型查询的生命周期编译错误

我尝试用泛型方法从结构一致但某列类型不同的SQLite表中取值,编写了查询方法后编译失败。

核心代码

async fn query<'a, 'r, T: DatabaseType<Item=T> + Decode<'r, Sqlite> + Type<Sqlite>>(&self, name: &'a str) -> Result<Vec<NamedValue<'a, T>>> {
    let mut connection = self.pool.acquire().await?;

    let mut rows = sqlx::query("Select id, value from table where name = $1")
        .bind(name)
        .fetch(&mut connection);

    let mut results = Vec::new();
    while let Some(row) = rows.try_next().await? {
        results.push(NamedValue {
            name,
            value: row.try_get("value")?
        })
    }

    Ok(results)
}

编译错误

borrowed value does not live long enough, argument requires that 'row' is borrowed for 'r

问题在于sqlx::Decode要求的生命周期'r被声明为函数签名的一部分,但这个生命周期仅对应迭代行时的临时资源。不能去掉该约束,因为try_get需要类型实现Decode才能工作。另外,解码后的值生命周期是static,需要让编译器确认解码时row的生命周期足够。


完整可复现代码

Cargo.toml

[package]
name = "sqlx-minimal-example"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "sqlite"] }
anyhow = "1.0"
futures = "0.3"

src/main.rs

use anyhow::Result;
use sqlx::{Decode, Row, Sqlite, SqlitePool, Type};
use futures::TryStreamExt;

#[tokio::main]
async fn main() -> Result<()> {
    println!("Hello, world!");
    Ok(())
}

struct NamedValue<'a ,T> {
    name: &'a str,
    value: T
}

struct SqliteBackend {
    pool: SqlitePool
}

trait DatabaseType {
    type Item;
}

impl DatabaseType for f32 {
    type Item = f32;
}

impl DatabaseType for i32 {
    type Item = i32;
}

impl SqliteBackend {
    async fn query<'a, 'r, T: DatabaseType<Item=T> + Decode<'r, Sqlite> + Type<Sqlite>>(&self, name: &'a str) -> Result<Vec<NamedValue<'a, T>>> {
        let mut connection = self.pool.acquire().await?;

        let mut rows = sqlx::query("Select id, value from table where name = $1")
            .bind(name)
            .fetch(&mut connection);

        let mut results = Vec::new();
        while let Some(row) = rows.try_next().await? {
            results.push(NamedValue {
                name,
                value: row.try_get("value")?
            })
        }

        Ok(results)
    }
}

解决方案

问题根源是函数签名中的'r生命周期被绑定到了整个函数外部,但实际上'r只需要在解码单个row的瞬间有效,且解码后的T不持有row的引用。可以通过**高阶生命周期(Higher-Ranked Trait Bounds, HRTBs)**约束T对任意生命周期'r都实现Decode<'r, Sqlite>,无需将'r暴露在函数签名中。

修改后的query函数签名如下:

async fn query<'a, T>(&self, name: &'a str) -> Result<Vec<NamedValue<'a, T>>>
where
    T: DatabaseType<Item = T> + for<'r> Decode<'r, Sqlite> + Type<Sqlite>,

解释

  • for<'r> Decode<'r, Sqlite> 表示:对于任意生命周期'r,T都能实现Decode<'r, Sqlite>。这告诉编译器,无论row的生命周期多短,T都能从其中解码,且解码后的T不会持有row的引用,符合值为static生命周期的前提。

替换后的完整impl SqliteBackend代码:

impl SqliteBackend {
    async fn query<'a, T>(&self, name: &'a str) -> Result<Vec<NamedValue<'a, T>>>
    where
        T: DatabaseType<Item = T> + for<'r> Decode<'r, Sqlite> + Type<Sqlite>,
    {
        let mut connection = self.pool.acquire().await?;

        let mut rows = sqlx::query("Select id, value from table where name = $1")
            .bind(name)
            .fetch(&mut connection);

        let mut results = Vec::new();
        while let Some(row) = rows.try_next().await? {
            results.push(NamedValue {
                name,
                value: row.try_get("value")?
            })
        }

        Ok(results)
    }
}

修改后编译器可正确推断生命周期,代码编译通过。


内容的提问来源于stack exchange,提问作者tamathews01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 01:50:48