为什么一个明显已经实现了该trait的类型没有实现它?

35

我正在尝试使用 Diesel 来查询 MySQL 数据库,并使用带有 Rocket 的 Handlebars 模板显示结果。

我在 models.rs 中有这个代码:

#[derive(Queryable, Serialize)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub text: String,
    pub published: bool,
}

cargo run 输出如下:

  --> src/main.rs:69:5
   |
69 |     Template::render("index", &results)
   |     ^^^^^^^^^^^^^^^^ the trait `serde::ser::Serialize` is not implemented for `tasty::models::Post`
   |
   = note: required because of the requirements on the impl of `serde::ser::Serialize` for `std::vec::Vec<tasty::models::Post>`
   = note: required by `rocket_contrib::Template::render`
在我的Cargo.toml文件中,我有以下内容:
[dependencies]
chrono = "0.3.0"
rocket = "0.2.8"
rocket_codegen = "0.2.8"
serde = "1.0.8"
serde_derive = "1.0.8"
serde_json = "1.0.2"
mysql = "11.1.2"
diesel = { version = "0.13.0", features = ["mysql","chrono"] }
diesel_codegen = { version = "0.13.0", features = ["mysql"] }
dotenv = "0.10.0"

[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["handlebars_templates"]

我已经阅读到目前为止 Diesel 不支持 Serialize,但我不确定。


1
寻求调试帮助的问题(“为什么这段代码不起作用?”)必须在问题本身中包含所需的行为、具体问题或错误以及最短的代码,以便在问题中重现它。——你的错误信息指向了你没有展示的代码,因此我们更难知道问题所在,因此我们被迫猜测,这浪费了你和我们的时间。 - Shepmaster
1个回答

45
一般的问题是代码有多个版本的 crate,每个 crate 提供不同版本的 traits。Rust 允许这样做是件好事,但是围绕它的错误信息很令人困惑
你的 crate 从版本 A 实现了 Serialize,但库在一个公共接口中使用了版本 B。这些 traits 不兼容,所以当你将实现 Serialize@A 的类型传递给需要 Serialize@B 的函数时,编译器会阻止你。
虽然你的示例是关于不同 traits 的,但这也可能发生在从 crate 重新导出的类型上。 cargo treeRust 1.44 中推出,非常有用,可以验证这是否是你的问题。它显示了所有依赖项及其版本。它甚至有一个 -d 标志来显示重复的依赖项!尽管这里没有显示,但它非常有用。
一般解决方法是在Cargo.toml中手动限制您的Serde版本以匹配其他依赖项:
serde = "0.9"
serde_derive = "0.9"
serde_json = "0.9"

有时这可能是不可能的,这种情况下,您可能需要催促创建者升级其依赖项。

工作示例

火箭

[dependencies]
chrono = "0.3.0"
rocket = "0.2.8"
rocket_codegen = "0.2.8"
serde = "1.0.8"
serde_derive = "1.0.8"
serde_json = "1.0.2"
mysql = "11.1.2"
diesel = { version = "0.13.0", features = ["mysql","chrono"] }
diesel_codegen = { version = "0.13.0", features = ["mysql"] }
dotenv = "0.10.0"

[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["handlebars_templates"]

rocket_contrib 0.2.8 依赖于 Serde 0.9,但你已经引入了 Serde 1.0。下面是从 cargo tree 中摘录的问题代码:

reproduction v0.1.0 (file:///private/tmp/reproduction)
├── rocket_contrib v0.2.8
│   ├── handlebars v0.25.3
│   │   └── serde_json v0.9.10
│   │       └── serde v0.9.15
│   ├── serde v0.9.15 (*)
│   └── serde_json v0.9.10 (*)
├── serde v1.0.8 (*)
├── serde_derive v1.0.8
│   ├── serde_derive_internals v0.15.1
└── serde_json v1.0.2 (*)

Rocket的即将推出的0.3版本应该允许使用Serde 1.0。

Iron / Bson / MongoDB

[dependencies]
bodyparser = "0.5"
bson = "0.8"
iron = "0.5"
jwt = "0.4"
mongodb = "0.3"
router = "0.5"
rust-crypto = "0.2"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
time = "0.1"

bodyparser 0.5 依赖于 Serde 0.8,MongoDB 已经引入了 0.9 版本,但是 crate 和 BSON 引入了 Serde 1.0。下面是从 cargo tree 中摘抄的问题代码:

reproduction v0.1.0 (file:///private/tmp/reproduction)
├── bodyparser v0.5.0
│   ├── serde v0.8.23
│   └── serde_json v0.8.6
│       └── serde v0.8.23 (*)
├── bson v0.8.0
│   ├── serde v1.0.8
│   ├── serde_json v1.0.2
│   │   └── serde v1.0.8 (*)
├── mongodb v0.3.0
│   ├── textnonce v0.6.0
│   │   ├── serde v0.9.15
│   │   └── serde_derive v0.9.15
├── serde v1.0.8 (*)
├── serde_derive v1.0.8
├── serde_json v1.0.2 (*)

Bodyparser 0.7.0 应该支持 Serde 1.0。textnonce 的状态不太清楚,但这个依赖可能是私有的,所以在这种情况下可能无关紧要。

Diesel / Chrono

chrono = "0.4.0"
diesel = { version = "0.13.0", features = [ "chrono", "sqlite" ] }
diesel_codegen = { version = "0.13.0", features = [ "sqlite" ] }
dotenv = "0.9.0"

当前版本的Chrono为0.4.0,但Diesel只知道如何序列化Chrono 0.3.0。
reproduction v0.1.0 (file:///private/tmp/reproduction)
├── chrono v0.4.0
├── diesel v0.13.0
│   ├── chrono v0.3.0

blowfish / block-cipher-trait

[dependencies]
blowfish = "0.2.1"
block-cipher-trait = "0.3.0"

reproduction v0.1.0 (file:///private/tmp/reproduction)
├── block-cipher-trait v0.3.0
│── blowfish v0.2.1
    ├── block-cipher-trait v0.2.0

conrod / piston2d-graphics

[dependencies]
piston_window = "0.74.0"
conrod = { version = "0.56.0", features = ["piston"] }

repro v0.1.0 (file:///private/tmp/repro)
├── conrod v0.56.0
│   ├── piston2d-graphics v0.23.0
└── piston_window v0.74.0
    ├── piston2d-graphics v0.24.0 (*)

actix / futures

[dependencies]
actix-web = "0.6.10"
futures = "0.2.1"

repro v0.1.0 (file:///private/tmp/repro)
├── actix-web v0.6.12
│   ├── actix v0.5.8
│   │   ├── futures v0.1.21
└── futures v0.2.1

一个光明的未来?

RFC 1977 提议在 Cargo 中引入“公共”和“私有”依赖的概念。如果你使用一个创建了公共接口并暴露了另一个创建的类型的 crate,Cargo 将确保你使用具有公共类型的 crate 的单一统一版本。


这个答案很好,cargo tree帮助我找到了我的问题。我去阅读了引用的RFC 1977,但我看不出它如何解决问题。在我的情况下,我很幸运依赖变化很小,所以我能够克隆冲突项目的github存储库并更新我的自己的分支中的依赖项。这显然不是一个能满足大多数情况的解决方案。由于这个答案是来自2017年,我想知道关于这个问题的当前情况是什么。 - palako
@palako 答案是最后编辑于17天前;我会关注热门话题。 - Shepmaster
@palako 公共/私有依赖项将有所帮助,因为Cargo将知道它能够在私有crate集群中选择任意版本,但可以确保公共依赖项使用单一统一版本 - Shepmaster
谢谢Shepmaster。但是public/private应该取决于crate的编写者,对吧?如果我在代码中使用了两个库,这两个库又使用了同一个依赖的不同版本,而且存在这个问题,我不知道如何解决它。 - palako
@palako 是的,这可能需要创建者使用它。但是,它可以作为默认公共实现,这种情况下,所有依赖项的版本都需要统一。它还可以允许最终用户定义未定义公共/私有边界的板条箱。对于您的示例,这完全取决于这些板条箱如何使用依赖项。 - Shepmaster
我正在处理 rkyv 和它对 bytecheck 和 bytecheck::CheckBytes 的使用。我发现 rkyv 重新导出了 bytecheck。错误[E0277]: 未满足 ArchivedFileList: rkyv::CheckBytes<DefaultValidator<'_>> 的 trait 约束 --> src/main.rs:220:18 | 220 | let entity = check_archived_root::<FileList>(&serialized_data).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ArchivedFileList 未实现 rkyv::CheckBytes<DefaultValidator<'_>> 这个 trait | - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接