返回已实现的Struct的泛型异步特性



我遇到了一个我认为很简单的问题。我仍在学习Rust,我想做以下事情:

我想创建一个异步特征(使用async-trait(,它将实例化一个DB连接实例,并返回实现该特征的结构。

mongo.rs

#[async_trait]
pub trait DB {
async fn init<T, E>() -> Result<T, E>;
}

然后:favorites.rs(见下面DB特性的实现(

use async_trait::async_trait;
use mongodb::Collection;
use rocket::form::FromForm;
use rocket::serde::ser::StdError;
use serde::{Deserialize, Serialize};
use std::error::Error;
use uuid::Uuid;
pub struct FavoritesDB {
collection: Collection<Favorite>,
}
#[derive(Debug)]
pub enum FavoritesError {
UnknownError(Box<dyn Error>),
}
// Conflicts with the one down below
// impl From<Box<dyn Error>> for FavoritesError {
//     fn from(err: Box<dyn Error>) -> FavoritesError {
//         FavoritesError::UnknownError(err)
//     }
// }
impl From<Box<dyn StdError>> for FavoritesError {
fn from(err: Box<dyn StdError>) -> FavoritesError {
FavoritesError::UnknownError(err)
}
}

#[async_trait]
impl mongo::DB for FavoritesDB {
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError> {
let main_db = mongo::init::<Favorite>("Favorites").await?;
let db = FavoritesDB {
collection: main_db.collection,
};
Ok(db)
}
}

这里有一系列问题:

1(

error[E0574]: expected struct, variant or union type, found type parameter `FavoritesDB`
--> srcdbfavorites.rs:41:18
|
41 |         let db = FavoritesDB {
|                  ^^^^^^^^^^^ not a struct, variant or union type
|
help: consider importing this struct instead
  1. 我尝试过手动实现From<Box<dyn tdError>>,但它与我所拥有的相冲突
error[E0277]: `?` couldn't convert the error to `FavoritesError`
--> srcdbfavorites.rs:40:65
|
40  |         let main_db = mongo::init::<Favorite>("Favorites").await?;
|                                                                 ^ the trait `From<Box<dyn StdError>>` is not implemented for `FavoritesError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, Box<dyn StdError>>>` for `Result<FavoritesDB, FavoritesError>`
note: required by `from_residual`
--> C:Usersasili.rustuptoolchainsnightly-2021-11-15-x86_64-pc-windows-msvclib/rustlib/src/rustlibrarycoresrcopstry_trait.rs:339:5
|
339 |     fn from_residual(residual: R) -> Self;
|     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider further restricting this bound
|
39  |     async fn init<FavoritesDB, FavoritesError + std::convert::From<std::boxed::Box<dyn std::error::Error>>>() -> Result<FavoritesDB, FavoritesError> {
|                                               ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Some errors have detailed explanations: E0277, E0282, E0574.
For more information about an error, try `rustc --explain E0277`.

为了获得更多上下文,这里是mongo.rs中包含的DBstructimpl(当前连接到本地MongoDB(

pub struct Database<T> {
client: mongodb::Database,
pub collection: Collection<T>,
}
impl<T> Database<T> {
pub async fn init() -> Result<mongodb::Database, Box<dyn Error>> {
let mut client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
client_options.app_name = Some("My App".to_string());
// Get a handle to the deployment.
let client = Client::with_options(client_options)?;
let db = client.database("rust-svelte");
return Ok(db);
}
}
pub async fn init<T>(collection: &str) -> Result<Database<T>, Box<dyn Error>> {
let client = Database::<T>::init().await?;
let collection = client.collection::<T>(collection);
let db = Database { client, collection };
Ok(db)
}

我已经在SO和Rust社区搜索了几天,我的谷歌Rust Fu不够好,无法发现问题所在。有什么想法吗?

您已经声明init接受两个泛型参数:TE

这意味着调用init的代码必须提供具体的类型来填充这些参数。例如,如果有人在使用你的库,那么他们编写init::<i64, ()>()是完全可行的,你的代码应该处理这个问题。

正因为如此,当你定义你的impl DB for FavouritesDB时,你会写下:

async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError>

这与写作没有什么不同:

async fn init<T, E>() -> Result<T, E>

您刚刚为类型参数提供了不同的名称,这些名称恰好与您可能正在尝试使用的结构相匹配。

更好的模式可能是关联类型。与调用方决定具体类型是什么(泛型就是这样(以及相关类型不同,在类型上实现trait设置类型。

这在Iterator之类的东西中很常见。Iterator没有通用参数,但只有一个关联类型Item。这是因为同时使用impl Iterator<String> for MyStructimpl Iterator<i64> for MyStruct是没有意义的。相反,我们希望为一个类型实现一次Iterator,并且该实现附带了它所期望的类型的定义。

所以类似这样的东西(为了简洁起见,我省略了异步,因为它似乎不是这里的一个因素(:

trait DB {
type InitOk;
type InitErr;
fn init() -> Result<Self::InitOk, Self::InitErr>;
}
impl Db for FavouritesDB {
type InitOk = FavouritesDB;
type InitErr = FavouritesError;
fn init() -> Result<Self::InitOk, Self::InitErr> {
// now you can reference FavouritesDB the struct, rather than the generic parameter
}
}

我还想补充一点,您可能不希望有InitOk类型,只返回Self,但如果您认为您可能希望结构能够创建不同的类型,那就由您决定了。

对于第2部分,Rust对泛型参数不做任何假设(除了Sized(。如果您希望Rust强制泛型具有某些属性,则必须添加绑定。

编译器在这里告诉您,它不能使用?运算符来自动转换,因为它不知道您的错误类型有From<Box<dyn Error>>实现。

如果您知道每个错误类型都将实现这一点,那么您可以将其添加为关联类型的绑定,如下所示:

trait DB {
type InitOk;
type InitErr: From<Box<dyn Error>>;
// ...
}

最新更新