实现 proc 宏时的循环包依赖

Posted

技术标签:

【中文标题】实现 proc 宏时的循环包依赖【英文标题】:Cyclic package dependency while implementing proc macro 【发布时间】:2019-06-02 22:22:16 【问题描述】:

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

为此,我有一个板条箱foo,其中包含我的“原始”结构(在本例中为P1P2),它们只能是可转储的。

接下来我有一个 foo_derive crate,其中包含程序宏本身。

因为我想支持多种格式,所以我有第三个板条箱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)`

我不知道正确的方法是什么,如何在这个 crate 中使用依赖项。我现在的是:

这当然是不可能的。

我错过了什么?我该怎么做才能打破依赖圈?


(mcve@github)

/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()

【问题讨论】:

你的派生箱不应该依赖任何东西,因为它只处理代码生成。 【参考方案1】:

感谢@Boiethious comment 和他的帮助in chat 我能够提出一个解决方案,其中包括引入一个新的板条箱foo_core,其中包含结构P1P2

所以我做的是:

foo 中删除P1P2 并将它们放入foo_corefoo_derive 中删除依赖关系foo_dump,因此它不再依赖于synquotefoofoo_dump 中添加foo_core 作为依赖项 将依赖foo_dump 添加到foo

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

最终的依赖链现在看起来像这样:

【讨论】:

如果您有更好的解决方案,请随时创建一个!我对任何可用的解决方案都感兴趣。 需要 foo_core 吗?你可以把东西放在 foo_dump 有趣...我认为可以将我需要的东西放在foo_dump 中。我必须看看我在现实世界中的例子,但是是的,这将是一种更简单的方法。

以上是关于实现 proc 宏时的循环包依赖的主要内容,如果未能解决你的问题,请参考以下文章

将 TranslateService 注入拦截器时的 Angular 循环依赖

自定义库名称与系统库相同时的CMake循环依赖错误

引用 IP 地址生成配置文件时的 Terraform 循环依赖问题

记一次spring boot项目启动时的依赖循环

golang中包循环依赖问题

objective-c启用ARC时的内存管理 (循环引用)