关于rust:实现proc宏时的循环软件包依赖关系

Cyclic package dependency while implementing proc macro

我尝试实现proc_macro Dump,它类似于serdes Serialize

为此,我有一个板条箱foo,其中包含我的"原始"结构(在这种情况下为P1P2),这些结构只能是可转储的。

接下来,我确实有一个foo_derive条板箱,其中包含程序宏本身。

因为我想支持多种格式,所以我有第三个板条箱foo_dump,其中包含Dump的特征定义(例如,可以转储此结构)和Dumper(这是后端应实现的功能)。
非常直截了当的到这一点。

现在,我想编译它时,出现以下错误:

1
2
3
4
5
$ 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)`

我不知道正确的方法是什么,如何在此板条箱中使用依赖项。我当前的是:

dependencies

这当然是不可能的。

我想念什么?我要怎么做才能打破依赖圈?

(mcve @ github)

/Cargo.toml

1
2
3
4
5
6
[workspace]
members = [
   "foo",
   "foo_derive",
   "foo_dump",
]

/foo/Cargo.toml

1
2
3
4
5
6
7
[package]
name ="foo"
version ="0.1.0"
edition ="2018"

[dependencies]
foo_derive = { path ="../foo_derive" }

/foo/src/lib.rs

1
2
3
4
5
6
7
8
9
10
use foo_derive::Dump;

struct P1;
struct P2;

#[derive(Dump)]
struct Bar {
    primitive_one: P1,
    primitive_two: P2,
}

/foo_dump/Cargo.toml

1
2
3
4
5
6
7
[package]
name ="foo_dump"
version ="0.1.0"
edition ="2018"

[dependencies]
foo = { path ="../foo" }

/foo_dump/src/lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

1
2
3
4
5
6
7
8
9
10
11
12
[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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()
}


感谢@Boiethious评论和他在聊天中的帮助,我得以提出一个解决方案,该解决方案涉及引入一个新的板条箱foo_core,其中包含结构P1P2

所以我做的是:

  • foo中删除P1P2并将它们放入foo_core
  • foo_derive中删除依赖项foo_dump,使其不再仅依赖于synquote
  • foofoo_dump中将foo_core添加为依赖项
  • 将依赖项foo_dump添加到foo

(您可以在git历史中查看更改的完整列表)。

现在,最终的依赖链如下所示:

depency graph