gpt4 book ai didi

rust - 无法在使用rust 柴油中使用通用参数实现特征

转载 作者:行者123 更新时间:2023-12-05 04:41:07 24 4
gpt4 key购买 nike

我定义了一个名为 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相关的代码,除了cargo.toml,我使用 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 后端),我希望如果您在 Cargo.toml 中更改添加更多后端,错误消息会发生变化。通过柴油机功能标志。

那么如何以支持所有后端的方式编写该特征绑定(bind)。编译器已经暗示了一个方向 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 => 与上一个绑定(bind)相同,需要命名 T .
  • U: Queryable<T::SqlType, Conn::Backend> => 那个很有趣。

那么让我们详细看看 `U: Queryable 绑定(bind):

  • U是您的查询返回的类型。所以bool在这种情况下。
  • T::SqlType是查询返回的sql类型,所以类型来自diesel::sql_types . exists返回一个 bool 值,所以 diesel::sql_type::Bool
  • Conn::Backend是你的通用B类型。

这意味着我们需要验证 bool: Queryable<diesels::sql_type::Bool, B> . There is an existing impl in diesel这需要 bool: FromSql<diesel::sql_types::Bool, B> .同样有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 轻松访问它.
  • 显然,您不应将密码作为明文存储在数据库中

关于rust - 无法在使用rust 柴油中使用通用参数实现特征,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70149016/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com