我有一个IR
的AST struct ,还有一个Data
的 struct .这些 node 中的每一个都由几乎相同类型的 node 组成,但每个 node 都有一些只属于它们的 node .一般来说,IR
可以包含像IfThenElse
或函数应用这样的逻辑运算,而Data
必须是更简单的记录.(但是,不要假设Data
是IR
的严格子集--整个问题要复杂得多.)
我希望这些树的类型为IR
,如果且仅当它们中的所有内容都类型为IR
.我可以对具有固定数量的子 node 的所有 node 执行此操作,但不能对动态调整大小的 node (如List
)执行此操作.
trait AST{} //Super trait that includes everything we're describing
trait IR{} //Tree that includes logic and operations
trait Data{} //Tree that only includes static data
为了避免大量冗余,我希望每种类型的 node 都指定一次,然后让它们可变地属于IR
和/或Data
.对于简单的 node ,足够简单:
//Literals can appear in any tree:
enum Literal{
Int(i32),
Bool(bool)
}
impl AST for Literal {}
impl Data for Literal {}
impl IR for Literal {}
//Variables can only appear in IR, not Data:
struct Variable(String);
impl AST for Variable {}
impl IR for Variable {}
//Assume we also have a type that is only valid as Data, not IR:
struct DataItem();
impl AST for DataItem {}
impl Data for DataItem {}
我们还有包含其他 node 的 node :
//IfThenElse is only IR, so the struct itself can have type requirements:
struct IfThenElse<Cond, Then, Else>(
Cond,
Then,
Else
);
impl<Cond : AST + IR, Then : AST + IR, Else : AST + IR> AST for IfThenElse<Cond , Then, Else> {}
impl<Cond : AST + IR, Then : AST + IR, Else : AST + IR> IR for IfThenElse<Cond , Then, Else> {}
//Tuples can be valid IR, Data, or both, depending on their children:
struct Tuple<T1, T2>(T1, T2);
impl<T1 : AST, T2 : AST> AST for Tuple<T1, T2>{}
impl<T1 : IR + AST, T2 : IR + AST> IR for Tuple<T1, T2>{}
impl<T1 : Data + AST, T2 : Data + AST> Data for Tuple<T1, T2>{}
到目前为止,一切都完全按照我希望的那样工作,如下面的代码所示:
//Functions to assert that the type checking actually works:
fn accept_ir<T : IR>(t : &T){}
fn accept_data<T : Data>(t : &T){}
fn accept_ast<T : AST>(t : &T){}
fn tuple_type_check() {
//Tuple with a Variable beneath it is IR:
let ir_t = Tuple(
Variable("x".to_string()),
Literal::Int(1)
);
accept_ir(&ir_t);
//Tuple with only literals is both IR and Data:
let both_t = Tuple(
Literal::Int(1),
Literal::Bool(true)
);
accept_ir(&both_t);
accept_data(&both_t);
//Tuples can be nested, and the type checking will still work:
let nested_ir_t = Tuple(
IfThenElse(
Literal::Bool(true),
Literal::Int(1),
Literal::Int(2)
),
Literal::Int(5)
);
accept_ir(&nested_ir_t);
//And it knows all of these are AST:
accept_ast(&ir_t);
accept_ast(&both_t);
accept_ast(&nested_ir_t);
//Tuples which contain a mix of Data-only and IR-only items are neither:
let malformed_t = Tuple(
DataItem(),
Variable("x".to_string()),
);
// accept_ir(&malformed_t); //Won't compile (as it shouldn't)
// accept_data(&malformed_t);
}
在上面的图中,您可以看到Tuple
个 node 在其子 node 为Data
时类型为Data
,当子 node 为IR
时为IR
,并且这对嵌套树递归工作.
当我试图为其中包含动态集合的类型获取相同的行为时,我遇到了困难:
struct List<T>(
Vec<T>
);
impl Data for List<Box<dyn Data>> {}
impl IR for List<Box<dyn IR>> {}
impl AST for List<Box<dyn AST>> {}
fn list_type_check() {
//A list of only data is data...
let data_l: List<Box<dyn Data>> = List( //..but I have to explicitly declare it as a Data list
vec![Box::new(Literal::Int(1)), Box::new(DataItem())]
);
accept_data(&data_l);
//A list of literals should be Data AND IR
let both_l: List<Box<dyn Data>> = List( //..but I have pick just one to declare it as
vec![Box::new(Literal::Int(1)), Box::new(Literal::Int(2))]
);
accept_data(&both_l);
//accept_ir(&both_l); //and the other one won't compile..
//Nesting works, but I have to declare the inner list as a list of dyn IR/Data too:
let inner_l: List<Box<dyn IR>> = List(
vec![
Box::new(Variable("x".to_string())),
Box::new(Variable("y".to_string())),
]
);
let outer_l : List<Box<dyn IR>> = List(
vec![
Box::new(inner_l),
Box::new(Variable("l".to_string())),
]
);
accept_ir(&outer_l);
//And none of these are also AST:
// accept_ast(&data_l);
// accept_ast(&both_l);
// accept_ast(&outer_l);
}
这并不是完全失败,但:
- 每个 node 都必须显式声明(好的推论已不复存在)
- 如果我试图把
AST
作为一个超级特征,它就会崩溃(trait IR: AST {}
) - 价值观不能同时属于这两个特征,即使它们的内容符合要求
我还怀疑,有很多方法可以让我迈出一步,但在future ,这种 struct 的用处会大大降低.