例如,我希望以下代码无法编译,因为 Foo
可以指向 Bar
可以指向 Foo
.
#[derive(NoCycles)]
struct Foo {
k: u32,
p: Option<Rc<Bar>>,
}
#[derive(NoCycles)]
struct Bar {
s: Option<Rc<Foo>>,
}
#[derive(NoCycles)]
struct Baz {
s: String,
}
如 Bar
已更改为 Option<Rc<Baz>>
,编译应该会成功,因为Foo
没有办法指向 Foo
.
最佳答案
我没有编写程序宏的经验,但我会尝试为 NoCycle
版本生成一个“平行宇宙”。 IE。对于每个结构 Foo
应该参加NoCycle
,会有一个“并行”结构 Foo_NoCycle
仅用于循环检测。
现在的想法是:结构 Foo_NoCycle
将从 Foo
自动生成,其成员将拥有 NoCycle
- Foo
中成员的并行类型. IE。以下结构
struct Foo {
k: u32,
p: Option<Rc<Bar>>,
}
将有平行 NoCycle
结构:struct Foo_NoCycle {
k: u32_NoCycle,
p: Option<Rc<Bar>>_NoCycle, // <- not real rust syntax
}
如您所见,上面的 - simpfy 附加后缀 _NoCycle
- 不会导致有效的 Rust 语法。因此,您可以引入一个特征,作为“正常”和 NoCycle
之间的桥梁。 -结构:trait NoCycleT {
type NoCycleType;
}
它的用法 - 展示给 Foo_NoCycle
- 会是这样的:struct Foo_NoCycle {
k: <u32 as NoCycleT>::NoCycleType,
p: <Option<Rc<Bar>> as NoCycleT>::NoCycleType
}
生成 Foo_NoCycle
来自 Foo
应该可以通过宏来实现。现在技巧来了:你告诉 rust 对于
u32
对应的NoCycle
-类型是 u32
, 而 Rc<Bar>
有 NoCycle
型Bar
:impl NoCycleT for u32 {
type NoCycle=u32;
}
impl<T: NoCycleT> NoCycleT for Rc<T> {
type NoCycle = T::NoCycleType;
}
这样,NoCycle
-types 导致真正的循环类型,防止编译。对于您的示例,
NoCycle
-structs 看起来像这样:struct Foo_NoCycle {
k: <u32 as NoCycleT>::NoCycleType, // == u32
p: <Option<Rc<Bar>> as NoCycleT>::NoCycleType, // == Bar_NoCycle
}
struct Bar_NoCycle {
s: <Option<Rc<Foo>> as NoCycleT>::NoCycleType, // == Foo_NoCycle
}
替换类型显示:struct Foo_NoCycle {
k: u32,
p: Bar_NoCycle,
}
struct Bar_NoCycle {
s: Foo_NoCycle,
}
这样,编译器看到 Foo_NoCycle
和 Bar_NoCycle
形成无法编译的循环类型依赖。这不是一个无需努力定义即可工作的解决方案
NoCycleT
用于基本类型,并定义 NoCycleT
对于诸如 Box
之类的事情, Rc
, Arc
, Vec
, Mutex
等。但是,我猜编译器会通知您缺少 impl
s 以便您可以实现 NoCycleT
对于实际需要的类型。
关于rust - 是否可以创建一个自定义派生来防止编译时类型之间的循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62972118/