了解 Diesel 中的特征边界误差
Understanding trait bound error in Diesel
我想编写一个函数,将一个类型插入数据库,其中数据库连接参数是通用的,以便它可以在多个后端上工作。
我想出了以下函数来使用通用连接插入对象:
pub fn create_label<C>(connection: &C, label: &model::Label)
where
C: Connection,
C::Backend: diesel::backend::Backend,
C::Backend: diesel::backend::SupportsDefaultKeyword,
{
diesel::insert(&label)
.into(schema::label::table)
.execute(connection);
}
如果我不包含 SupportsDefaultKeyword
约束,函数将无法编译。使用 SqliteConnection
作为连接参数调用它时,出现以下错误:
database::create_label(&db_conn, &label);
^^^^^^^^^^^^^^^^^^^^^^ the trait
'diesel::backend::SupportsDefaultKeyword' is not implemented for
'diesel::sqlite::Sqlite'
这意味着使用 SqliteConnection
插入数据不起作用。显然不是这种情况,并且进一步更改 create_label
使其直接使用 SqliteConnection
就可以了。
pub fn create_label(connection: &SqliteConnection, label: &model::Label) {
diesel::insert(&label)
.into(schema::label::table)
.execute(connection);
}
为什么通用函数需要 SupportsDefaultKeyword
约束而采用 SqliteConnection
的函数不需要?
这里 minimal example 说明了这个问题。根据评论,main.rs
的第 60 行不会因上述错误而编译,而第 61 行会编译:
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
mod schema {
table! {
labels {
id -> Integer,
name -> VarChar,
}
}
}
mod model {
use schema::labels;
#[derive(Debug, Identifiable, Insertable)]
#[table_name = "labels"]
pub struct Label {
pub id: i32,
pub name: String,
}
}
use diesel::ExecuteDsl;
use diesel::Connection;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
pub fn create_label<C>(connection: &C, label: &model::Label)
where
C: Connection,
C::Backend: diesel::backend::Backend,
C::Backend: diesel::backend::SupportsDefaultKeyword,
{
diesel::insert(label)
.into(schema::labels::table)
.execute(connection)
.expect("nope");
}
pub fn create_label_sqlite(connection: &SqliteConnection, label: &model::Label) {
diesel::insert(label)
.into(schema::labels::table)
.execute(connection)
.expect("nope");
}
pub fn establish_connection() -> SqliteConnection {
let url = "test.db";
SqliteConnection::establish(&url).expect(&format!("Error connecting to {}", url))
}
fn main() {
let label = model::Label {
id: 1,
name: String::from("test"),
};
let conn = establish_connection();
create_label(&conn, &label); /* Does not compile */
create_label_sqlite(&conn, &label); /*Compiles */
}
[dependencies]
diesel = { version = "0.16.0", features = ["sqlite"] }
diesel_codegen = "0.16.0"
Diesel 函数 execute
有多个具体实现。此处相关的两个是:
impl<'a, T, U, Op, Ret, Conn, DB> ExecuteDsl<Conn, DB> for BatchInsertStatement<T, &'a [U], Op, Ret>
where
Conn: Connection<Backend = DB>,
DB: Backend + SupportsDefaultKeyword,
InsertStatement<T, &'a [U], Op, Ret>: ExecuteDsl<Conn>,
impl<'a, T, U, Op, Ret> ExecuteDsl<SqliteConnection> for BatchInsertStatement<T, &'a [U], Op, Ret>
where
InsertStatement<T, &'a U, Op, Ret>: ExecuteDsl<SqliteConnection>,
T: Copy,
Op: Copy,
Ret: Copy,
从这两个可以看出,SQLite 的实现是特殊的。我对 Diesel 的细节了解不多,不知道为什么,但我猜 SQLite 缺少 default 关键字。
您可以改为重新制定适用于该特定语句的任何连接的要求:
use diesel::query_builder::insert_statement::InsertStatement;
pub fn create_label<C>(connection: &C, label: &model::Label)
where
C: Connection,
for<'a> InsertStatement<schema::labels::table, &'a model::Label>: ExecuteDsl<C>,
{
diesel::insert(label)
.into(schema::labels::table)
.execute(connection)
.expect("nope");
}
我想编写一个函数,将一个类型插入数据库,其中数据库连接参数是通用的,以便它可以在多个后端上工作。
我想出了以下函数来使用通用连接插入对象:
pub fn create_label<C>(connection: &C, label: &model::Label)
where
C: Connection,
C::Backend: diesel::backend::Backend,
C::Backend: diesel::backend::SupportsDefaultKeyword,
{
diesel::insert(&label)
.into(schema::label::table)
.execute(connection);
}
如果我不包含 SupportsDefaultKeyword
约束,函数将无法编译。使用 SqliteConnection
作为连接参数调用它时,出现以下错误:
database::create_label(&db_conn, &label); ^^^^^^^^^^^^^^^^^^^^^^ the trait 'diesel::backend::SupportsDefaultKeyword' is not implemented for 'diesel::sqlite::Sqlite'
这意味着使用 SqliteConnection
插入数据不起作用。显然不是这种情况,并且进一步更改 create_label
使其直接使用 SqliteConnection
就可以了。
pub fn create_label(connection: &SqliteConnection, label: &model::Label) {
diesel::insert(&label)
.into(schema::label::table)
.execute(connection);
}
为什么通用函数需要 SupportsDefaultKeyword
约束而采用 SqliteConnection
的函数不需要?
这里 minimal example 说明了这个问题。根据评论,main.rs
的第 60 行不会因上述错误而编译,而第 61 行会编译:
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
mod schema {
table! {
labels {
id -> Integer,
name -> VarChar,
}
}
}
mod model {
use schema::labels;
#[derive(Debug, Identifiable, Insertable)]
#[table_name = "labels"]
pub struct Label {
pub id: i32,
pub name: String,
}
}
use diesel::ExecuteDsl;
use diesel::Connection;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
pub fn create_label<C>(connection: &C, label: &model::Label)
where
C: Connection,
C::Backend: diesel::backend::Backend,
C::Backend: diesel::backend::SupportsDefaultKeyword,
{
diesel::insert(label)
.into(schema::labels::table)
.execute(connection)
.expect("nope");
}
pub fn create_label_sqlite(connection: &SqliteConnection, label: &model::Label) {
diesel::insert(label)
.into(schema::labels::table)
.execute(connection)
.expect("nope");
}
pub fn establish_connection() -> SqliteConnection {
let url = "test.db";
SqliteConnection::establish(&url).expect(&format!("Error connecting to {}", url))
}
fn main() {
let label = model::Label {
id: 1,
name: String::from("test"),
};
let conn = establish_connection();
create_label(&conn, &label); /* Does not compile */
create_label_sqlite(&conn, &label); /*Compiles */
}
[dependencies]
diesel = { version = "0.16.0", features = ["sqlite"] }
diesel_codegen = "0.16.0"
Diesel 函数 execute
有多个具体实现。此处相关的两个是:
impl<'a, T, U, Op, Ret, Conn, DB> ExecuteDsl<Conn, DB> for BatchInsertStatement<T, &'a [U], Op, Ret>
where
Conn: Connection<Backend = DB>,
DB: Backend + SupportsDefaultKeyword,
InsertStatement<T, &'a [U], Op, Ret>: ExecuteDsl<Conn>,
impl<'a, T, U, Op, Ret> ExecuteDsl<SqliteConnection> for BatchInsertStatement<T, &'a [U], Op, Ret>
where
InsertStatement<T, &'a U, Op, Ret>: ExecuteDsl<SqliteConnection>,
T: Copy,
Op: Copy,
Ret: Copy,
从这两个可以看出,SQLite 的实现是特殊的。我对 Diesel 的细节了解不多,不知道为什么,但我猜 SQLite 缺少 default 关键字。
您可以改为重新制定适用于该特定语句的任何连接的要求:
use diesel::query_builder::insert_statement::InsertStatement;
pub fn create_label<C>(connection: &C, label: &model::Label)
where
C: Connection,
for<'a> InsertStatement<schema::labels::table, &'a model::Label>: ExecuteDsl<C>,
{
diesel::insert(label)
.into(schema::labels::table)
.execute(connection)
.expect("nope");
}