Reference Rust中计算图节点的重载添加

Reference Rust中计算图节点的重载添加,reference,rust,operator-overloading,Reference,Rust,Operator Overloading,我正在创建一个计算图,一个将方程建模为图的程序,这样就可以区分方程。我重载了操作符,因此添加图节点会创建新的图节点。然而,我发现自己在和借书人打交道。代码如下: use std::ops::Add; #[derive(Debug)] pub enum Expr<'a> { Constant(f32), // ^^^--this is a simplified version. In the real version, //

我正在创建一个计算图,一个将方程建模为图的程序,这样就可以区分方程。我重载了操作符,因此添加图节点会创建新的图节点。然而,我发现自己在和借书人打交道。代码如下:

use std::ops::Add;

#[derive(Debug)]
pub enum Expr<'a> {
    Constant(f32),
    //       ^^^--this is a simplified version. In the real version,
    //            Constant holds large matrices and I would like to 
    //            avoid copying it. 
    Add(&'a Node<'a>, &'a Node<'a>),
}

#[derive(Debug)]
pub struct Node<'a> {
    pub body: Expr<'a>
}

impl<'a> Add for &'a Node<'a> {
    type Output = Node<'a>;
    fn add(self, other: &'a Node<'a>) -> Node<'a> {
        Node { body: Expr::Add(self, other) }
    }
}

fn main() {
    let a = Node { body: Expr::Constant(1.) };
    let f: Node = &(&a + &a) + &a;
    println!("f: {:#?}", f);
}
我使用引用是因为我想避免复制
节点
结构。这里的代码稍微简化了一点;在我使用的版本中,这些可以保存大量数据

我尝试过静态引用和
Rc
指针,但没有成功。当我尝试使用
Rc
指针时,编译器抱怨重载
Add

fn add(self, other: &'a Expr<'a>) -> Expr<'a> {
    Expr::Constant(42.0)
}
error[E0117]:对于任意类型,只能实现当前板条箱中定义的特征
-->功能rs:86:1
|
86 |针对Rc的简单添加{
|^impl不在板条箱内使用类型
|
=注:IML未引用此板条箱中定义的任何类型
目前,我只是为每一个非常混乱的操作定义新变量:

let f1 = &a + &a;
...
let f = &a + f1

理想的情况是,如果我可以完全避免使用引用(这将使代码更干净)。至少,我需要某种方法从添加现有节点生成新节点。

让我们首先认识到
节点和
Expr
是冗余的:

use std::ops::Add;

#[derive(Debug)]
pub enum Expr<'a> {
    Constant(f32),
    Add(&'a Expr<'a>, &'a Expr<'a>),
}

impl<'a> Add for &'a Expr<'a> {
    type Output = Expr<'a>;
    fn add(self, other: &'a Expr<'a>) -> Expr<'a> {
        Expr::Add(self, other)
    }
}

fn main() {
    let a = Expr::Constant(1.);
    let f = &(&a + &a) + &a;
    println!("f: {:#?}", f);
}
然后我们调用它并将其作为表达式的前半部分返回:

let f = &(&a + &a) + &a;
//       ^-------- here
常量
存储在哪里?只有一个没有所有者的临时值。当你进行第二次加法时,你试图引用一个必须在
f
有效的整个时间内都存在的值。除了该表达式一结束,该值就会被删除,因为没有任何东西可以拥有它。因此你不能做这种事

此外,
&'a Expr
,我认为有两件事:

  • 这应该是
    Foo
    ——我们对某个有引用的东西有引用,它们有不同的生命周期
  • 后者在这里不起作用——解释起来很复杂,但最终你会得到类似的结果

    #[derive(Debug)]
    pub enum Expr<'e, 'd> {
        Constant(f32),
        Add(&'e Expr<'e, 'd>, &'e Expr<'e, 'd>),
    }
    
    如果希望表达式树拥有大数据以及对子树的引用,则可以按照注释中的建议使用
    Rc
    。每当克隆数据时,只有引用计数器递增,因此不会制作大副本:

    use std::ops::Add;
    use std::rc::Rc;
    
    type LargeMatrix = Vec<u8>;
    
    #[derive(Clone, Debug)]
    pub enum Expr {
        Constant(Rc<LargeMatrix>),
        Add(Rc<Expr>, Rc<Expr>),
    }
    
    impl Add for Expr {
        type Output = Expr;
        fn add(self, other: Expr) -> Expr {
            Expr::Add(Rc::new(self), Rc::new(other))
        }
    }
    
    fn main() {
        let a = Expr::Constant(Rc::new(LargeMatrix::new()));
        let f = (a.clone() + a.clone()) + a;
        println!("f: {:#?}", f);
    }
    
    使用std::ops::Add;
    使用std::rc::rc;
    类型LargeMatrix=Vec;
    #[派生(克隆、调试)]
    发布枚举表达式{
    常数(Rc),
    加上(Rc,Rc),,
    }
    Expr的impl Add{
    类型输出=Expr;
    fn添加(自身,其他:Expr)->Expr{
    Expr::Add(Rc::new(self),Rc::new(other))
    }
    }
    fn main(){
    设a=Expr::Constant(Rc::new(LargeMatrix::new());
    设f=(a.clone()+a.clone())+a;
    println!(“f:{:?}”,f);
    }
    
    然后,您可以选择为引用实现添加:

    impl<'a> Add for &'a Expr {
        type Output = Expr;
        fn add(self, other: &'a Expr) -> Expr {
            Expr::Add(Rc::new(self.clone()), Rc::new(other.clone()))
        }
    }
    

    impl感谢大家的帮助和建议。我没有意识到克隆
    Rc
    并不能克隆内部值。因此,我可以按如下方式重新实现我的代码:

    use std::ops::Add;
    use std::rc::Rc;
    
    #[derive(Debug)]
    pub enum Expr {
        Constant(f32),
        //       ^^^--this is a simplified version. In the real version,
        //            Constant holds large matrices and I would like to 
        //            avoid copying it. 
        Add(Node, Node),  // USED TO BE ADD(&NODE, &NODE)
    }
    
    #[derive(Debug, Clone)] // CLONE IS NEW
    pub struct Node {      
        pub body: Rc<Expr> // CHANGED TO RC
    }
    
    // THIS IS NEW
    impl Add for Node {
        type Output = Node;
        fn add(self, other: Node) -> Node { &self + &other }
    }
    
    impl<'a> Add for &'a Node {
        type Output = Node;
        fn add(self, other: &Node) -> Node {
            Node { 
              body: Rc::new(Expr::Add(self.clone(), other.clone()))
          }
        }
    }
    
    fn main() {
        let a = Node { body: Rc::new(Expr::Constant(1.)) };
        let f: Node = &a + &a + a; // <-- basically, the desired outcome
        println!("f: {:#?}", f);
    }
    
    使用std::ops::Add;
    使用std::rc::rc;
    #[导出(调试)]
    发布枚举表达式{
    常数(f32),
    //^^^——这是一个简化版本。在真实版本中,
    //常数包含大型矩阵,我想
    //避免复制它。
    Add(Node,Node),//用于Add(&Node,&Node)
    }
    #[派生(调试,克隆)]//克隆是新的
    发布结构节点{
    发布正文:Rc//已更改为Rc
    }
    //这是新的
    节点的impl Add{
    类型输出=节点;
    fn add(self,other:Node)->Node{&self+&other}
    }
    impl节点{
    节点{
    正文:Rc::new(Expr::Add(self.clone(),other.clone())
    }
    }
    }
    fn main(){
    设a=Node{body:Rc::new(Expr::Constant(1.))};
    
    让f:Node=&a+&a+a;//为什么不按照错误消息告诉您的那样做并使用
    let
    绑定?
    让f=&a+&a;让f2=&f+&a;
    。为什么首先要存储对
    节点的引用,而不是直接存储它们?为什么不为非引用实现
    添加
    ?谢谢您的建议ion@Shepmaster。因此,我目前使用的是
    f1=…;f2=…
    。在处理大型复杂方程时,这会使代码难以阅读。存储引用的原因是,使用add作为非引用将需要一个副本,这对性能是有问题的(我对代码做了一些简化,但在实际版本中,图形节点可以包含大型矩阵)。使用
    Rc
    时出现了什么问题?我无法用Rc重载运算符。我用更多细节编辑了原始帖子。@ethabrooks
    Rc
    实现了
    Deref
    Clone
    ,因此使用
    Rc
    你应该能够编写
    &foo+&bar
    foo.Clone()+bar.Clone()
    ,具体取决于您是实施
    添加
    以供参考还是添加值。
    use std::ops::Add;
    use std::rc::Rc;
    
    #[derive(Debug)]
    pub enum Expr {
        Constant(f32),
        //       ^^^--this is a simplified version. In the real version,
        //            Constant holds large matrices and I would like to 
        //            avoid copying it. 
        Add(Node, Node),  // USED TO BE ADD(&NODE, &NODE)
    }
    
    #[derive(Debug, Clone)] // CLONE IS NEW
    pub struct Node {      
        pub body: Rc<Expr> // CHANGED TO RC
    }
    
    // THIS IS NEW
    impl Add for Node {
        type Output = Node;
        fn add(self, other: Node) -> Node { &self + &other }
    }
    
    impl<'a> Add for &'a Node {
        type Output = Node;
        fn add(self, other: &Node) -> Node {
            Node { 
              body: Rc::new(Expr::Add(self.clone(), other.clone()))
          }
        }
    }
    
    fn main() {
        let a = Node { body: Rc::new(Expr::Constant(1.)) };
        let f: Node = &a + &a + a; // <-- basically, the desired outcome
        println!("f: {:#?}", f);
    }