我遇到了一个我认为很简单的问题。我仍在学习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
- 我尝试过手动实现
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
中包含的DBstruct
和impl
(当前连接到本地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
接受两个泛型参数:T
和E
。
这意味着调用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 MyStruct
和impl 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>>;
// ...
}