我正试图既有我的蛋糕,又吃它与IR的特征和枚举表示.

我的 idea 是,我想要的许多东西都可以通过拥有enum个版本的特征来实现,这些版本的分支只包含AST node 上的数据,然后包含相关的特征.traitenum版本相互转换,给了我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 内容上实现这一点的最佳方式是什么?(即,当它们在BoxVec或仅在拥有的T中时?
  • 或者,这是一个真正将生命周期放在enum个元素上的场合,而枚举只包含对拥有的数据的引用吗?

EDIT: Why not just enums?

  • 我发现,如果必须将类型参数附加到递归枚举,当子级上的类型参数可能与父级上的类型参数不同时,递归枚举会变得很麻烦

  • 我希望有几个这样的特征/枚举,其中大多数将共享它们的大多数变体;我希望为这些变体实现一个共享函数

  • 虽然我希望特定的变量是"动态的",因为我需要能够对它进行模式匹配,但我仍然希望对存在的 node 的风格进行静态的、类型级别的推理-比如"一个元组是数据当它的子 node 是数据".

  • 同时属于两种不同类型的 struct 会很有帮助(例如,我的数据 struct 也是IR struct ).

话虽如此,我承认这可能是个坏主意,我可能会制造比我要解决的更多的问题.不过,我仍然希望让它以这种方式工作,以便与仅枚举的模型进行公平的比较,并防止该技术在其他上下文中被证明是有用的.

推荐答案

特征上的方法可以接受self: Box<Self>个参数,并且可以在特征对象上分派:

trait IR: 'static + std::fmt::Debug {
    fn evaluate(&self) -> Box<dyn Data>;
    fn to_ir_enum(self: Box<Self>) -> IREnum;
}

impl<T: IR + ?Sized> IR for Box<T> {
    fn evaluate(&self) -> Box<dyn Data> {
        T::evaluate(&**self)
    }
    fn to_ir_enum(self: Box<Self>) -> IREnum {
        T::to_ir_enum(*self)
    }
}

trait Data: IR {
    fn to_data_enum(self: Box<Self>) -> DataEnum;
}

impl<T: Data + ?Sized> Data for Box<T> {
    fn to_data_enum(self: Box<Self>) -> DataEnum {
        T::to_data_enum(*self)
    }
}

#[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: Box<Self>) -> IREnum {
        IREnum::BoolLiteral(self.0)
    }
}
impl Data for BoolLiteral {
    fn to_data_enum(self: Box<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: Box<Self>) -> IREnum {
        IREnum::Tuple(
            self.0
                .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: Box<Self>) -> DataEnum {
        DataEnum::Tuple(
            self.0
                .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: Box<Self>) -> IREnum {
        IREnum::IfThenElse {
            condition: Box::new(self.condition),
            then_branch: Box::new(self.then_branch),
            else_branch: Box::new(self.else_branch),
        }
    }
}

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);
}

Playground.

这里有一点效率低下,因为我们甚至装箱Box,所以我们以Box<Box<...>>结束.这不应该是你关心的问题,因为你总是框的值一般无论如何,但有办法解决它,如果你需要的.

Rust相关问答推荐

基于对vec值的引用从该值中删除该值

如何仅使用http机箱发送http请求?

如何go 除铁 rust 中路径组件的第一项和最后一项?

重写Rust中的方法以使用`&;mut self`而不是`mut self`

用于判断整数块是否连续的SIMD算法.

为什么实现特征的对象期望比具体对象有更长的生命周期?

在使用粗粒度锁访问的数据 struct 中使用 RefCell 是否安全?

使用启用优化的 alloc 会导致非法指令崩溃

详尽的匹配模式绑定

Rust 1.70 中未找到 Trait 实现

从 HashMap>, _> 中删除的生命周期问题

在每个循环迭代中删除borrow

将 Futures 的生命周期特征绑定到 fn 参数

强制特征仅在 Rust 中的给定类型大小上实现

为什么允许重新分配 String 而不是 *&String

为什么会出现无法移出可变引用后面的 `self.x`错误?

使用部分键从 Hashmap 中检索值

如何将切片推入数组?

类型组的通用枚举

返回 &str 但不是 String 时,borrow 时间比预期长