我正试图既有我的蛋糕,又吃它与IR的特征和枚举表示.
我的 idea 是,我想要的许多东西都可以通过拥有enum
个版本的特征来实现,这些版本的分支只包含AST node 上的数据,然后包含相关的特征.trait
和enum
版本相互转换,给了我enum
的动态匹配以及特征的灵活性和威力.缺点是一些样板文件,包括enum
个版本和来回传递,但这似乎是值得的成本.
这是almost在工作,除了一件事:出于其他原因,(我想?)我需要在Box<T : Trait + ?Sized>
上实现这些特性.?Sized
是个问题,因为它阻止了我(我想?)对已装箱对象调用‘self方法’.
我有一个更进一步的 idea ,我可以通过在特征上有一个私有助手函数来解决这个问题,这将需要&mut self
而不是self
,因此Box
个隐式函数可以调用它.然后,实际的 struct 支持的impl
可以通过将它们的内容(应该只是一些元数据和几个框指针)移动到返回值来使用&mut
版本.从本质上讲,这&mut
个函数将销毁它们被调用的任何东西,但是(我认为?)可见性可以通过一种方式进行限制,除非在拥有的数据上实现函数,否则它们永远不会被调用.
您可以在下面代码的顶部看到这方面的大体概念,以及更多的实现.
trait IR : 'static + std::fmt::Debug{
fn evaluate(&self) -> Box<dyn Data>;
fn to_ir_enum(self) -> IREnum;
fn to_ir_enum_mut(&mut self) -> IREnum;
}
impl<T : IR + ?Sized> IR for Box<T>{
fn evaluate(&self) -> Box<dyn Data>{
self.as_ref().evaluate()
}
fn to_ir_enum(mut self) -> IREnum{
//(*self).to_ir_enum() //If I could do this it would be easy, but T is not sized!
self.to_ir_enum_mut()
}
fn to_ir_enum_mut(&mut self) -> IREnum{
self.as_mut().to_ir_enum_mut()
}
}
trait Data : IR{
fn to_data_enum(self) -> DataEnum;
fn to_data_enum_mut(&mut self) -> DataEnum;
}
impl<T : Data + ?Sized> Data for Box<T>{
fn to_data_enum(mut self) -> DataEnum{
self.to_data_enum_mut()
}
fn to_data_enum_mut(&mut self) -> DataEnum{
self.as_mut().to_data_enum_mut()
}
}
#[derive(Debug, Copy, Clone)]
struct BoolLiteral(bool);
impl IR for BoolLiteral{
fn evaluate(&self) -> Box<dyn Data>{
Box::new(BoolLiteral(self.0))
}
fn to_ir_enum(self) -> IREnum{
IREnum::BoolLiteral(self.0)
}
fn to_ir_enum_mut(&mut self) -> IREnum{
IREnum::BoolLiteral(self.0)
}
}
impl Data for BoolLiteral{
fn to_data_enum(self) -> DataEnum{
DataEnum::BoolLiteral(self.0)
}
fn to_data_enum_mut(&mut self) -> DataEnum{
DataEnum::BoolLiteral(self.0)
}
}
#[derive(Debug)]
struct Tuple<T>(Vec<T>);
impl<T : IR> IR for Tuple<T>{
fn evaluate(&self) -> Box<dyn Data>{
let data_vec = self.0.iter().map(|x| x.evaluate()).collect();
let data_tuple = Tuple(data_vec);
Box::new(data_tuple)
}
fn to_ir_enum(self) -> IREnum{
IREnum::Tuple(self.0.into_iter().map(|x| {let boxed : Box<dyn IR> = Box::new(x); boxed} ).collect())
}
fn to_ir_enum_mut(&mut self) -> IREnum{
//let mut new_vec: Vec<Box<dyn IR>> = Vec::new();
let new_vec : Vec<T> = self.0.drain(..).collect();
IREnum::Tuple(new_vec.into_iter().map(|x| {let boxed : Box<dyn IR> = Box::new(x); boxed} ).collect())
}
}
impl<T : Data> Data for Tuple<T>{
fn to_data_enum(self) -> DataEnum{
DataEnum::Tuple(self.0.into_iter().map(|x| {let boxed : Box<dyn Data> = Box::new(x); boxed} ).collect())
}
fn to_data_enum_mut(&mut self) -> DataEnum{
let new_vec: Vec<T> = self.0.drain(..).collect();
DataEnum::Tuple(new_vec.into_iter().map(|x| {let boxed : Box<dyn Data> = Box::new(x); boxed} ).collect())
}
}
#[derive(Debug)]
struct IfThenElse<T1, T2, T3>{
condition : T1,
then_branch : T2,
else_branch : T3
}
impl<T1 : IR, T2 : IR, T3 : IR> IR for IfThenElse<T1, T2, T3>{
fn evaluate(&self) -> Box<dyn Data>{
let condition: Box<dyn Data> = self.condition.evaluate();
let condition_enum = condition.to_data_enum();
match condition_enum{
DataEnum::BoolLiteral(true) => self.then_branch.evaluate(),
DataEnum::BoolLiteral(false) => self.else_branch.evaluate(),
_ => panic!("IfThenElse condition not a BoolLiteral")
}
}
fn to_ir_enum(self) -> IREnum{
IREnum::IfThenElse{
condition: Box::new(self.condition),
then_branch: Box::new(self.then_branch),
else_branch: Box::new(self.else_branch)
}
}
fn to_ir_enum_mut(&mut self) -> IREnum{
todo!();
}
}
enum DataEnum{
BoolLiteral(bool),
Tuple(Vec<Box<dyn Data>>),
}
impl DataEnum{
pub fn to_trait(self) -> Box<dyn Data>{
match self{
DataEnum::BoolLiteral(b) => Box::new(BoolLiteral(b)),
DataEnum::Tuple(v) => Box::new(Tuple(v))
}
}
}
enum IREnum{
BoolLiteral(bool),
Tuple(Vec<Box<dyn IR>>),
IfThenElse{
condition: Box<dyn IR>,
then_branch: Box<dyn IR>,
else_branch: Box<dyn IR>
},
}
impl IREnum{
pub fn to_trait(self) -> Box<dyn IR>{
match self{
IREnum::BoolLiteral(b) => Box::new(BoolLiteral(b)),
IREnum::Tuple(v) => Box::new(Tuple(v)),
IREnum::IfThenElse{condition, then_branch, else_branch} => Box::new(IfThenElse{
condition: condition,
then_branch: then_branch,
else_branch: else_branch
})
}
}
}
pub fn run() {
let lit_true = BoolLiteral(true);
let lit_false = BoolLiteral(false);
let tuple_1 = Tuple(vec![Box::new(lit_true), Box::new(lit_false)]);
let tuple_2 = Tuple(vec![Box::new(lit_false), Box::new(lit_true)]);
let if_then_else = IfThenElse{
condition: lit_true,
then_branch: tuple_1,
else_branch: tuple_2
};
let tuple_3 = Tuple(vec![Box::new(lit_true), Box::new(lit_false)]);
let tuple_4: Box<dyn IR> = Box::new(Tuple(vec![Box::new(lit_true), Box::new(lit_false)]));
let big_tuple = Tuple(vec![tuple_4, Box::new(lit_true)]);
let if_then_else_outer = IfThenElse{
condition: lit_false,
then_branch: if_then_else,
else_branch: big_tuple
};
let result = if_then_else_outer.evaluate();
println!("Result: {:?}", result);
}
上面的代码实际上是可以编译和工作的,这就是为什么我非常确信我正在做的事情是可能的.Tuple
通过将其向量清空成一个拥有的向量来实现该行为,并使用该向量创建返回类型;文字可以简单地复制它们拥有的原始数据.
但是,您会注意到:
fn to_ir_enum_mut(&mut self) -> IREnum{
todo!();
}
我相信我可以做一些事情,使IfThenElse
框的内容,或要求所有这些类型有某种默认值,或什么的,但我觉得我在这里的分支非常遥远,并重新实现已经存在的东西,以某种更简单的形式.所以:
- 有什么更简单的方法可以让我在
Self : ?Sized
的时候实现to_enum(self)
个方法吗? - 如果不是,当我知道引用的对象可以有效地销毁时,是否有某种方法可以通用地实现这
to_enum_mut(&mut self)
个函数? - 如果不是,在各种形式的 struct 内容上实现这一点的最佳方式是什么?(即,当它们在
Box
、Vec
或仅在拥有的T
中时? - 或者,这是一个真正将生命周期放在
enum
个元素上的场合,而枚举只包含对拥有的数据的引用吗?
EDIT: Why not just enums?个
-
我发现,如果必须将类型参数附加到递归枚举,当子级上的类型参数可能与父级上的类型参数不同时,递归枚举会变得很麻烦
-
我希望有几个这样的特征/枚举,其中大多数将共享它们的大多数变体;我希望为这些变体实现一个共享函数
-
虽然我希望特定的变量是"动态的",因为我需要能够对它进行模式匹配,但我仍然希望对存在的 node 的风格进行静态的、类型级别的推理-比如"一个元组是数据当它的子 node 是数据".
-
同时属于两种不同类型的 struct 会很有帮助(例如,我的数据 struct 也是IR struct ).
话虽如此,我承认这可能是个坏主意,我可能会制造比我要解决的更多的问题.不过,我仍然希望让它以这种方式工作,以便与仅枚举的模型进行公平的比较,并防止该技术在其他上下文中被证明是有用的.