无法在生锈柴油中使用通用参数实现特征
Cannot implement trait with generic param in rust diesel
我定义了一个名为 exists_by_id_and_password
的特征。我不想用具体的数据库后端来实现它,所以一些通用参数被添加到 DB
结构中。但编译器报错:
type mismatch resolving `<C as Connection>::Backend == Pg`
expected type parameter `B`
found struct `Pg`
required because of the requirements on the impl of `LoadQuery<C, bool>` for `diesel::query_builder::SelectStatement<(), query_builder::select_clause::SelectClause<Exists<diesel::query_builder::SelectStatement<table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<And<diesel::expression::operators::Eq<columns::username, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>, diesel::expression::operators::Eq<columns::password, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>>>>>>`
很奇怪提到的错误Pg
,我认为这是由diesel生成的模式引起的,因为我的代码中没有任何与postgres相关的代码,除了[=15] =], 我用 features=["postgres"]
.
进口柴油
下面是我的代码:
use diesel::select;
use crate::authenticator::AuthDB;
use crate::diesel::dsl::exists;
use crate::diesel::prelude::*;
use crate::schema::users;
pub struct DB<C, B>
where
C: diesel::Connection<Backend = B>,
B: diesel::backend::Backend,
{
conn: C,
}
impl<C, B> AuthDB for DB<C, B>
where
C: diesel::Connection<Backend = B>,
B: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Bool>,
{
fn exists_by_id_and_password(
&self,
id: String,
password: String,
) -> Result<bool, crate::error::Error> {
Ok(select(exists(
users::dsl::users.filter(
users::dsl::username
.eq(id)
.and(users::dsl::password.eq(password)),
),
))
.get_result::<bool>(&self.conn)?)
}
}
我想知道,可以在没有具体后端类型的情况下实现特征
Rustc 确实已经作为错误消息的一部分发出了根本问题是什么:
required because of the requirements on the impl of `LoadQuery<C, bool>` for `diesel::query_builder::SelectStatement<(), query_builder::select_clause::SelectClause<Exists<diesel::query_builder::SelectStatement<table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<And<diesel::expression::operators::Eq<columns::username, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>, diesel::expression::operators::Eq<columns::password, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>>>>>>`
这可以理解为 LoadQuery<C, bool>
需要为您的查询实现(long 类型代表您在编译时的查询)。
现在 rustc 尤其是错误报告部分真的很聪明。它知道可能存在的 impl 并尝试通过在此处建议“简单”解决方案来提供帮助。这就是为什么它建议 Backend
应该是 Pg
因为为此找到了该特征的唯一含义。这是唯一存在的实现,因为你只启用了一个后端(postgres 后端),我希望如果你通过 diesels 功能标志在 Cargo.toml
中添加更多后端,错误消息会改变。
那么如何以支持所有后端的方式编写该特征绑定。编译器已经用 required because …
行提示了方向。您需要声明 LoadQuery
已为您的查询实现。不幸的是,现在您不能只从错误消息中复制相应的类型,因为这不是 public API 中可用的类型。所以这似乎是一个死胡同。 The documentation lists LoadQuery
特征的两个潜在含义。我们对第二个感兴趣,因为我们的类型不是 SqlQuery
。现在查看所需的边界,我们可以观察到以下内容:
Conn: Connection
=> 已经完成,
Conn::Backend: HasSqlType<T::SqlType>
=> 已经完成,
T: AsQuery + RunQueryDsl<Conn>
=> 这要求我们可以命名 T
,这是我们的查询类型。那是不可能的,所以让我们暂时忽略它。
T::Query: QueryFragment<Conn::Backend> + QueryId
=> 同上次绑定,需要命名为T
.
U: Queryable<T::SqlType, Conn::Backend>
=> 那个很有趣。
所以让我们详细看看`U: Queryable bound:
U
是您的查询返回的类型。所以在这种情况下 bool
。
T::SqlType
是查询返回的 sql 类型,因此是来自 diesel::sql_types
的类型。 exists
returns 一个布尔值,所以 diesel::sql_type::Bool
Conn::Backend
是您的通用 B
类型。
这意味着我们需要验证bool: Queryable<diesels::sql_type::Bool, B>
。 There is an existing impl in diesel which requires that bool: FromSql<diesel::sql_types::Bool, B>
. Again there are existing impls 也是如此,但请注意,这些仅针对特定后端实现。所以这里发生的是 rustc 看到那个 impl 并得出结论,这是唯一可用的 impl。这意味着 B
必须是 Pg
否则这个函数将无效。现在有另一种方法可以让 rustc 相信这个 trait impl 存在,那就是将它添加为 trait bound。这为您提供了以下工作实现:
impl<C, B> AuthDB for DB<C, B>
where
C: diesel::Connection<Backend = B>,
B: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Bool>,
bool: diesel::deserialize::FromSql<diesel::sql_types::Bool, B>
{
fn exists_by_id_and_password(
&self,
id: String,
password: String,
) -> QueryResult<bool> {
Ok(diesel::select(diesel::dsl::exists(
users::dsl::users.filter(
users::dsl::username
.eq(id)
.and(users::dsl::password.eq(password)),
),
))
.get_result::<bool>(&self.conn)?)
}
}
旁注:
如果启用额外的后端,我会收到以下错误消息
error[E0277]: the trait bound `bool: FromSql<Bool, B>` is not satisfied
--> src/main.rs:39:10
|
39 | .get_result::<bool>(&self.conn)?)
| ^^^^^^^^^^ the trait `FromSql<Bool, B>` is not implemented for `bool`
|
= note: required because of the requirements on the impl of `Queryable<Bool, B>` for `bool`
= note: required because of the requirements on the impl of `LoadQuery<C, bool>` for `diesel::query_builder::SelectStatement<(), query_builder::select_clause::SelectClause<Exists<diesel::query_builder::SelectStatement<table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<And<diesel::expression::operators::Eq<columns::username, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>, diesel::expression::operators::Eq<columns::password, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>>>>>>`
help: consider extending the `where` bound, but there might be an alternative better way to express this requirement
|
25 | B: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Bool>, bool: FromSql<Bool, B>
| ~~~~~~~~~~~~~~~~~~~~~~~~
这恰好表明了上述特征界限。
另外两个半相关的旁注:
- 没有理由明确地将后端作为
DB
结构的第二个参数。您可以随时通过 C::Backend
. 轻松访问它
- 显然你不应该将密码作为明文存储在你的数据库中
我定义了一个名为 exists_by_id_and_password
的特征。我不想用具体的数据库后端来实现它,所以一些通用参数被添加到 DB
结构中。但编译器报错:
type mismatch resolving `<C as Connection>::Backend == Pg`
expected type parameter `B`
found struct `Pg`
required because of the requirements on the impl of `LoadQuery<C, bool>` for `diesel::query_builder::SelectStatement<(), query_builder::select_clause::SelectClause<Exists<diesel::query_builder::SelectStatement<table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<And<diesel::expression::operators::Eq<columns::username, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>, diesel::expression::operators::Eq<columns::password, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>>>>>>`
很奇怪提到的错误Pg
,我认为这是由diesel生成的模式引起的,因为我的代码中没有任何与postgres相关的代码,除了[=15] =], 我用 features=["postgres"]
.
下面是我的代码:
use diesel::select;
use crate::authenticator::AuthDB;
use crate::diesel::dsl::exists;
use crate::diesel::prelude::*;
use crate::schema::users;
pub struct DB<C, B>
where
C: diesel::Connection<Backend = B>,
B: diesel::backend::Backend,
{
conn: C,
}
impl<C, B> AuthDB for DB<C, B>
where
C: diesel::Connection<Backend = B>,
B: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Bool>,
{
fn exists_by_id_and_password(
&self,
id: String,
password: String,
) -> Result<bool, crate::error::Error> {
Ok(select(exists(
users::dsl::users.filter(
users::dsl::username
.eq(id)
.and(users::dsl::password.eq(password)),
),
))
.get_result::<bool>(&self.conn)?)
}
}
我想知道,可以在没有具体后端类型的情况下实现特征
Rustc 确实已经作为错误消息的一部分发出了根本问题是什么:
required because of the requirements on the impl of `LoadQuery<C, bool>` for `diesel::query_builder::SelectStatement<(), query_builder::select_clause::SelectClause<Exists<diesel::query_builder::SelectStatement<table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<And<diesel::expression::operators::Eq<columns::username, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>, diesel::expression::operators::Eq<columns::password, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>>>>>>`
这可以理解为 LoadQuery<C, bool>
需要为您的查询实现(long 类型代表您在编译时的查询)。
现在 rustc 尤其是错误报告部分真的很聪明。它知道可能存在的 impl 并尝试通过在此处建议“简单”解决方案来提供帮助。这就是为什么它建议 Backend
应该是 Pg
因为为此找到了该特征的唯一含义。这是唯一存在的实现,因为你只启用了一个后端(postgres 后端),我希望如果你通过 diesels 功能标志在 Cargo.toml
中添加更多后端,错误消息会改变。
那么如何以支持所有后端的方式编写该特征绑定。编译器已经用 required because …
行提示了方向。您需要声明 LoadQuery
已为您的查询实现。不幸的是,现在您不能只从错误消息中复制相应的类型,因为这不是 public API 中可用的类型。所以这似乎是一个死胡同。 The documentation lists LoadQuery
特征的两个潜在含义。我们对第二个感兴趣,因为我们的类型不是 SqlQuery
。现在查看所需的边界,我们可以观察到以下内容:
Conn: Connection
=> 已经完成,Conn::Backend: HasSqlType<T::SqlType>
=> 已经完成,T: AsQuery + RunQueryDsl<Conn>
=> 这要求我们可以命名T
,这是我们的查询类型。那是不可能的,所以让我们暂时忽略它。T::Query: QueryFragment<Conn::Backend> + QueryId
=> 同上次绑定,需要命名为T
.U: Queryable<T::SqlType, Conn::Backend>
=> 那个很有趣。
所以让我们详细看看`U: Queryable
U
是您的查询返回的类型。所以在这种情况下bool
。T::SqlType
是查询返回的 sql 类型,因此是来自diesel::sql_types
的类型。exists
returns 一个布尔值,所以diesel::sql_type::Bool
Conn::Backend
是您的通用B
类型。
这意味着我们需要验证bool: Queryable<diesels::sql_type::Bool, B>
。 There is an existing impl in diesel which requires that bool: FromSql<diesel::sql_types::Bool, B>
. Again there are existing impls 也是如此,但请注意,这些仅针对特定后端实现。所以这里发生的是 rustc 看到那个 impl 并得出结论,这是唯一可用的 impl。这意味着 B
必须是 Pg
否则这个函数将无效。现在有另一种方法可以让 rustc 相信这个 trait impl 存在,那就是将它添加为 trait bound。这为您提供了以下工作实现:
impl<C, B> AuthDB for DB<C, B>
where
C: diesel::Connection<Backend = B>,
B: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Bool>,
bool: diesel::deserialize::FromSql<diesel::sql_types::Bool, B>
{
fn exists_by_id_and_password(
&self,
id: String,
password: String,
) -> QueryResult<bool> {
Ok(diesel::select(diesel::dsl::exists(
users::dsl::users.filter(
users::dsl::username
.eq(id)
.and(users::dsl::password.eq(password)),
),
))
.get_result::<bool>(&self.conn)?)
}
}
旁注:
如果启用额外的后端,我会收到以下错误消息
error[E0277]: the trait bound `bool: FromSql<Bool, B>` is not satisfied
--> src/main.rs:39:10
|
39 | .get_result::<bool>(&self.conn)?)
| ^^^^^^^^^^ the trait `FromSql<Bool, B>` is not implemented for `bool`
|
= note: required because of the requirements on the impl of `Queryable<Bool, B>` for `bool`
= note: required because of the requirements on the impl of `LoadQuery<C, bool>` for `diesel::query_builder::SelectStatement<(), query_builder::select_clause::SelectClause<Exists<diesel::query_builder::SelectStatement<table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<And<diesel::expression::operators::Eq<columns::username, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>, diesel::expression::operators::Eq<columns::password, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>>>>>>`
help: consider extending the `where` bound, but there might be an alternative better way to express this requirement
|
25 | B: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Bool>, bool: FromSql<Bool, B>
| ~~~~~~~~~~~~~~~~~~~~~~~~
这恰好表明了上述特征界限。
另外两个半相关的旁注:
- 没有理由明确地将后端作为
DB
结构的第二个参数。您可以随时通过C::Backend
. 轻松访问它
- 显然你不应该将密码作为明文存储在你的数据库中