我正试图实现以下特征。
#[async_trait]
pub trait ConnectionPoolTrait<'a: 'b, 'b, T: Client<'a, 'b>>: Clone + Send + Sync {
type T;
async fn client(&'a self) -> Result<Self::T>;
async fn migrate(&self, path: &str) -> Result<()>;
}
当我试图编译代码时,我得到了以下错误。
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements
--> src/connection_pool.rs:53:14
|
53 | async fn client(&'a self) -> Result<Self::T> {
| ______________^
54 | | Ok(client::Client::new(self.pool.get().await?))
55 | | }
| |_____^
|
note: first, the lifetime cannot outlive the lifetime `'b` as defined on the impl at 51:14...
--> src/connection_pool.rs:51:14
|
51 | impl<'a: 'b, 'b, T: Client<'a, 'b>> ConnectionPoolTrait<'a, 'b, T> for ConnectionPool {
| ^^
note: ...so that the types are compatible
--> src/connection_pool.rs:53:14
|
53 | async fn client(&'a self) -> Result<Self::T> {
| ______________^
54 | | Ok(client::Client::new(self.pool.get().await?))
55 | | }
| |_____^
= note: expected `client::ClientTrait<'_, '_>`
found `client::ClientTrait<'a, 'b>`
note: but, the lifetime must be valid for the lifetime `'async_trait` as defined on the method body at 50:1...
--> src/connection_pool.rs:50:1
|
50 | #[async_trait]
| ^^^^^^^^^^^^^^
note: ...so that the expression is assignable
--> src/connection_pool.rs:53:50
|
53 | async fn client(&'a self) -> Result<Self::T> {
| __________________________________________________^
54 | | Ok(client::Client::new(self.pool.get().await?))
55 | | }
| |_____^
= note: expected `std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Output = std::result::Result<client::Client<'_>, Error>> + std::marker::Send + 'async_trait)>>`
found `std::pin::Pin<std::boxed::Box<dyn std::future::Future<Output = std::result::Result<client::Client<'_>, Error>> + std::marker::Send>>`
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
这看起来像是与async_trait的冲突问题,但我对如何解决这个问题有点困惑。我的动机是为我的数据库连接创建一个可模拟的接口。我发现一个论坛说这可能是一个编译器的bug 此处希望不是这样。
以下是完整的文件代码
use crate::{client, configuration, Configuration, Pool, Result, Client};
use async_trait::async_trait;
use std::{
fs,
marker::{Send, Sync},
};
const SQL_EXTENSION: &str = "sql";
#[derive(Clone)]
pub struct ConnectionPool {
pool: Pool,
}
impl ConnectionPool {
pub async fn new(cfg: Configuration) -> Result<ConnectionPool> {
let manager =
bb8_postgres::PostgresConnectionManager::new(cfg.build()?, tokio_postgres::tls::NoTls);
let pool: Pool = bb8::Pool::builder()
.max_size(configuration::POOL_SIZE)
.build(manager)
.await?;
if environment::in_development() {
println!("Connected to database.");
}
Ok(ConnectionPool { pool })
}
pub async fn client(&self) -> Result<client::Client<'_>> {
Ok(client::Client::new(self.pool.get().await?))
}
pub async fn migrate(&self, path: &str) -> Result<()> {
let mut sql_files = files::by_extension(path, SQL_EXTENSION);
sql_files.sort();
let client = self.client().await?;
for file_path in sql_files.iter() {
let sql = fs::read_to_string(file_path)?;
client.batch(&sql).await?;
}
Ok(())
}
}
#[async_trait]
pub trait ConnectionPoolTrait<'a: 'b, 'b, T: Client<'a, 'b>>: Clone + Send + Sync {
type T;
async fn client(&'a self) -> Result<Self::T>;
async fn migrate(&self, path: &str) -> Result<()>;
}
#[async_trait]
impl<'a: 'b, 'b, T: Client<'a, 'b>> ConnectionPoolTrait<'a, 'b, T> for ConnectionPool {
type T = client::Client<'a>;
async fn client(&'a self) -> Result<Self::T> {
Ok(client::Client::new(self.pool.get().await?))
}
async fn migrate(&self, path: &str) -> Result<()> {
let mut sql_files = files::by_extension(path, SQL_EXTENSION);
sql_files.sort();
let client = self.client().await?;
for file_path in sql_files.iter() {
let sql = fs::read_to_string(file_path)?;
client.batch(&sql).await?;
}
Ok(())
}
}
我相信这是rust中GAT的一个问题,它还没有完成。根据我的理解,GAT's将提供将trait作为类型参数的能力,但是目前这种行为还在开发中,不支持。