由于你的帖子并没有真正包含一个最小的示例,我构建了以下示例:
table! {
users {
id -> Integer,
name -> Text,
}
}
fn apply_filters<C, V>(
mut query: users::BoxedQuery<diesel::sqlite::Sqlite>,
column: C,
value: V,
) -> users::BoxedQuery<diesel::sqlite::Sqlite>
{
// just use a single method here for now as other methods
// can be easily added later
query = query.filter(column.eq(value));
query
}
fn main() {
let q = users::table.into_boxed();
let q = apply_filters(q, users::id, 42);
apply_filters(q, users::name, "Foo");
println!("Hello, world!");
}
现在,在我们讨论“解决方案”之前,首先要提醒一下:为diesel编写抽象代码是可能的,但需要处理大量的特性边界。因此,在开始处理这类代码之前,请确保您对rusts-trait系统的工作原理有很好的了解。
现在需要回答的第一件事是如何限制类型
C
和
V
。我们可以在这里查看柴油机文件,发现有一个名为
Column
,这似乎适合我们的用例
C
。否则,我们也可以看看
ExpressionMethods::eq
方法,看看它是
implemented for all types
该工具
Expression
.
专栏
暗示
表达式
所以我们在那里很好。
For
五、
我们再看一遍
eq
方法,并查看参数是否受约束
T: AsExpression<Self::SqlType>,
这表明我们需要限制
五、
同样的特质。
这给我们留下了以下起始特征界限:
C: Column
和
V: AsExpression<C::SqlType>
.
如果我们尝试编译器抱怨“trait”
diesel::sql_types::SqlType
未实现
<C as diesel::Expression>::SqlType
“,因此我们添加了建议的trait bound:
C::SqlType: SqlType
如果我们尝试编译器再次抱怨“方法
eq
类型参数存在
C
,但其特征界限未得到满足”。现在,它给出了无意义的限制建议
C
到
Iterator
。帮助还包含以下块:
= note: the following trait bounds were not satisfied:
`<C as diesel::Expression>::SqlType: SingleValue`
which is required by `C: diesel::ExpressionMethods`
`<C as diesel::Expression>::SqlType: SingleValue`
which is required by `&C: diesel::ExpressionMethods`
`&mut C: diesel::Expression`
which is required by `&mut C: diesel::ExpressionMethods`
`C: Iterator`
which is required by `&mut C: Iterator`
所以,让我们试试
C: SingleValue
相反,这是一种柴油机的特性。
如果我们尝试让编译器再次抱怨“特性绑定”
C: ValidGrouping<()>
不满意”,并建议添加
C: 有效分组<()>
.
如果我们尝试让编译器再次抱怨“特性绑定”
<V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression: ValidGrouping<()>
不满意”,并建议添加该约束。现在可以更简短地写为
diesel::dsl::AsExpr<V, C>: ValidGrouping<()>
如果我们尝试让编译器再次抱怨“特性绑定”
<C as ValidGrouping<()>>::IsAggregate: MixedAggregates<<<V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression as ValidGrouping<()>>::IsAggregate>
不满意”,并建议添加另一个特征界限。建议的界限可以通过使用更容易地编写
<C as ValidGrouping<()>>::IsAggregate:MixedAggregates<<dsl::AsExpr<V, C> as ValidGrouping<()>>::IsAggregate>
如果我们再次尝试,编译器将再次抱怨“特性绑定”
<<C as ValidGrouping<()>>::IsAggregate as MixedAggregates<<<V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression as ValidGrouping<()>>::IsAggregate>>::Output: MixedAggregates<diesel::expression::is_aggregate::No>
不满意”,并建议另一个特征界限。不幸的是,这个建议是一个陷阱。相反,我们需要将最后一个界限修改为
<C as ValidGrouping<()>>::IsAggregate:MixedAggregates<<dsl::AsExpr<V, C> as ValidGrouping<()>>::IsAggregate, Output = is_aggregate::No>
如果我们这样做,编译器会再次抱怨“特性绑定”
<<C as diesel::Expression>::SqlType as diesel::sql_types::SqlType>::IsNull: OneIsNullable<<<C as diesel::Expression>::SqlType as diesel::sql_types::SqlType>::IsNull>
不满意”,并建议另一个长特征界限。这可以通过使用“所需的
expression::operators::Eq<C, <V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression>
实施
diesel::Expression
“改为行。这给了我们以下界限:
dsl::Eq<C, V>: Expression
如果我们这样做,编译器会再次抱怨“特性绑定”
<expression::grouped::Grouped<expression::operators::Eq<C, <V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression>> as diesel::Expression>::SqlType: BoolOrNullableBool
不满意”,并建议另一个特征界限。
如果我们尝试让编译器再次抱怨“特性绑定”
C: AppearsOnTable<users::table>
不满意”,并建议添加另一个特征边界。该边界有点帮助,但最好在以下注释行中基于特征边界之一添加一个边界:“必需
表达式::运算符::Eq<C、 <V作为AsExpression<&书信电报;C作为柴油::表达式>:SqlType>>::表达式>
实施
AppearsOnTable<users::table>
“.这导致了界限
dsl::Eq<C, V>: AppearsOnTable<users::table>
如果我们尝试让编译器再次抱怨“特性绑定”
C: QueryFragment<Sqlite>
不满意”。再次,最好添加绑定到
dsl::Eq
基于所需的原因行。
如果我们尝试编译器再次抱怨“
C
无法在线程之间安全地发送”。同样,最好添加绑定到的trait
dsl::方程式
如果我们尝试让编译器抱怨“关联的类型”
<V as AsExpression<<C as diesel::Expression>::SqlType>>::Expression
可能活得不够长”,并表明在
BoxedQuery
类型。所以我们添加一个命名的生命周期
'a
并限制相关类型的人至少能活多久
一
。对发出相同的错误消息
C
所以我们也在那里添加了界限。
添加这些边界后,代码最终会编译。这给出了以下代码:
fn apply_filters<'a, C, V>(
mut query: users::BoxedQuery<'a, diesel::sqlite::Sqlite>,
column: C,
value: V,
) -> users::BoxedQuery<diesel::sqlite::Sqlite>
where
C: Column + ValidGrouping<()> + 'a,
V: AsExpression<C::SqlType>,
C::SqlType: SqlType + SingleValue,
dsl::AsExpr<V, C>: ValidGrouping<()> + 'a,
<C as ValidGrouping<()>>::IsAggregate: MixedAggregates<
<dsl::AsExpr<V, C> as ValidGrouping<()>>::IsAggregate,
Output = is_aggregate::No,
>,
dsl::Eq<C, V>:
Expression + AppearsOnTable<users::table> + QueryFragment<diesel::sqlite::Sqlite> + Send,
<dsl::Eq<C, V> as Expression>::SqlType: BoolOrNullableBool,
{
query = query.filter(column.eq(value));
query
}
前5个边界是所有运算符都需要的一般边界。最后两个边界(两行均包含
dsl::方程式
)是您在此函数中使用的运算符特定的边界。如果使用多个运算符,则需要为其他运算符复制和调整这些行。例如,支持
.ne()
需要以下附加边界:
dsl::NotEq<C, V>:
Expression + AppearsOnTable<users::table> + QueryFragment<diesel::sqlite::Sqlite> + Send,
<dsl::NotEq<C, V> as Expression>::SqlType: BoolOrNullableBool,
其总体信息是:
-
编写这样的代码是可能的
-
你通常需要一些耐心,你需要仔细检查编译器输出,看看接下来要添加什么
-
您通常不想在应用程序中编写该代码,因为它相当复杂