实现 proc 宏时的循环包依赖
Cyclic package dependency while implementing proc macro
我尝试实现一个 proc_macro Dump
,它类似于 serdes Serialize
。
为此,我有一个板条箱 foo
,其中包含我的 "primitive" 结构(在本例中为 P1
和 P2
),它应该只能被转储。
接下来我有一个包含程序宏本身的 foo_derive
板条箱。
因为我想支持多种格式,所以我有第三个板条箱 foo_dump
,其中包含 Dump
(例如,可以转储此结构)和 Dumper
(这是后端应该实现的东西)。
直到这一点都非常简单。
当我现在要编译它时,我得到这个错误:
$ cargo build
error: cyclic package dependency: package `foo v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo)` depends on itself. Cycle:
package `foo v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo)`
... which is depended on by `foo_dump v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo_dump)`
... which is depended on by `foo_derive v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo_derive)`
我不知道正确的方法是什么,如何使用这个板条箱中的依赖项。我现在的是:
这当然是不可能的。
我错过了什么?我要怎么做才能打破依赖圈?
/Cargo.toml
[workspace]
members = [
"foo",
"foo_derive",
"foo_dump",
]
/foo/Cargo.toml
[package]
name = "foo"
version = "0.1.0"
edition = "2018"
[dependencies]
foo_derive = { path = "../foo_derive" }
/foo/src/lib.rs
use foo_derive::Dump;
struct P1;
struct P2;
#[derive(Dump)]
struct Bar {
primitive_one: P1,
primitive_two: P2,
}
/foo_dump/Cargo.toml
[package]
name = "foo_dump"
version = "0.1.0"
edition = "2018"
[dependencies]
foo = { path = "../foo" }
/foo_dump/src/lib.rs
use foo::{P1, P2};
pub trait Dumper {
fn dump_p1(&mut self, value: &P1);
fn dump_p2(&mut self, value: &P2);
}
pub trait Dump {
fn dump<D: Dumper>(&self, d: D);
}
impl Dump for P1 {
fn dump<D: Dumper>(&self, d: D) {
d.dump_p1(self);
}
}
impl Dump for P2 {
fn dump<D: Dumper>(&self, d: D) {
d.dump_p2(self);
}
}
/foo_derive/Cargo.toml
[package]
name = "foo_derive"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
syn = "*"
quote = "*"
foo_dump = { path = "../foo_dump" }
/foo_derive/src/lib.rs
extern crate proc_macro;
use quote::quote;
use proc_macro::TokenStream;
use syn::DeriveInput;
#[proc_macro_derive(Dump)]
pub fn derive_dump(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as DeriveInput);
let name = &input.ident;
quote!(
impl foo_dump::Dump for #name {
fn dump<D: foo_dump::Dumper>(&self, d: D) {
unimplemented!()
}
}
).into()
}
感谢 and his help in chat 我能够想出一个解决方案,它引入了一个包含结构 P1
和 P2
的新板条箱 foo_core
。
所以我所做的是:
- 从
foo
中删除 P1
和 P2
并将它们放入 foo_core
- 正在从
foo_derive
中删除依赖项 foo_dump
,因此它仅依赖于 syn
和 quote
- 在
foo
和 foo_dump
中添加 foo_core
作为依赖项
- 将依赖项
foo_dump
添加到 foo
(您可以在 git history 中查看完整的更改列表)。
最终的依赖链现在看起来像这样:
我尝试实现一个 proc_macro Dump
,它类似于 serdes Serialize
。
为此,我有一个板条箱 foo
,其中包含我的 "primitive" 结构(在本例中为 P1
和 P2
),它应该只能被转储。
接下来我有一个包含程序宏本身的 foo_derive
板条箱。
因为我想支持多种格式,所以我有第三个板条箱 foo_dump
,其中包含 Dump
(例如,可以转储此结构)和 Dumper
(这是后端应该实现的东西)。
直到这一点都非常简单。
当我现在要编译它时,我得到这个错误:
$ cargo build
error: cyclic package dependency: package `foo v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo)` depends on itself. Cycle:
package `foo v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo)`
... which is depended on by `foo_dump v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo_dump)`
... which is depended on by `foo_derive v0.1.0 (/tmp/tmp.u34pI5J6qd/example/foo_derive)`
我不知道正确的方法是什么,如何使用这个板条箱中的依赖项。我现在的是:
这当然是不可能的。
我错过了什么?我要怎么做才能打破依赖圈?
/Cargo.toml
[workspace]
members = [
"foo",
"foo_derive",
"foo_dump",
]
/foo/Cargo.toml
[package]
name = "foo"
version = "0.1.0"
edition = "2018"
[dependencies]
foo_derive = { path = "../foo_derive" }
/foo/src/lib.rs
use foo_derive::Dump;
struct P1;
struct P2;
#[derive(Dump)]
struct Bar {
primitive_one: P1,
primitive_two: P2,
}
/foo_dump/Cargo.toml
[package]
name = "foo_dump"
version = "0.1.0"
edition = "2018"
[dependencies]
foo = { path = "../foo" }
/foo_dump/src/lib.rs
use foo::{P1, P2};
pub trait Dumper {
fn dump_p1(&mut self, value: &P1);
fn dump_p2(&mut self, value: &P2);
}
pub trait Dump {
fn dump<D: Dumper>(&self, d: D);
}
impl Dump for P1 {
fn dump<D: Dumper>(&self, d: D) {
d.dump_p1(self);
}
}
impl Dump for P2 {
fn dump<D: Dumper>(&self, d: D) {
d.dump_p2(self);
}
}
/foo_derive/Cargo.toml
[package]
name = "foo_derive"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
syn = "*"
quote = "*"
foo_dump = { path = "../foo_dump" }
/foo_derive/src/lib.rs
extern crate proc_macro;
use quote::quote;
use proc_macro::TokenStream;
use syn::DeriveInput;
#[proc_macro_derive(Dump)]
pub fn derive_dump(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as DeriveInput);
let name = &input.ident;
quote!(
impl foo_dump::Dump for #name {
fn dump<D: foo_dump::Dumper>(&self, d: D) {
unimplemented!()
}
}
).into()
}
感谢 P1
和 P2
的新板条箱 foo_core
。
所以我所做的是:
- 从
foo
中删除P1
和P2
并将它们放入foo_core
- 正在从
foo_derive
中删除依赖项foo_dump
,因此它仅依赖于syn
和quote
- 在
foo
和foo_dump
中添加 - 将依赖项
foo_dump
添加到foo
foo_core
作为依赖项
(您可以在 git history 中查看完整的更改列表)。
最终的依赖链现在看起来像这样: