0

I'm trying to build an object for nested function calls. The idea is to write something based on simple common expressions such as f64 + f64. The Expr object then builds something similar to a parsed mathematical expression by implementing it's own arithmetic operations and acting on its children. Theoretically, one would then call the upper Expr object and it will in turn call its children and so own until an Expr with no children is found and the result can be propagated backwards.

However, I am unable to satisfy the compiler regarding moved objects (The code below doesn't compile)... I would appreciate any help!

use std::ops::Add;

pub struct Expr {
    children: (Box<Option<Expr>>, Box<Option<Expr>>), //children expressions
    func: Box<dyn Fn(f64, f64) -> f64>, //expr's function
}

impl Add<Expr> for Expr {
    type Output = Expr;

    fn add(self, rhs: Expr) -> Self::Output {
        let children = (Box::new(Some(self)), Box::new(Some(rhs)));
        let mut res = Expr {
            children,
            func: Box::new(|l, r| -> f64 { 1. }), //dummy function to be replaced
        };
        res.func = Box::new(
            |l, r| -> f64 {
                (&res.children.0.unwrap().func)(l, r) + (&res.children.1.unwrap().func)(l, r)
            }
        );
        res
    }
}
Adam
  • 743
  • 1
  • 6
  • 11
  • 2
    You make the struct `res` self-referential and then try to move it, which [doesn't work](https://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and-a-reference-to-that-value-in-the-same-struct). – Sven Marnach Feb 25 '21 at 15:13
  • 2
    You should either have `func` take ownership of the two `Expr`s and remove `children` entirely, or `func` should be simple like `|l, r| l + r` where the children are calculated separately and then passed to `func`. – kmdreko Feb 25 '21 at 15:43

1 Answers1

2

The problem is that res.func is stored in a Box on the heap, that itself is not connected to res.children, even though both of the boxes are stored in the same Expr. If the function would know about this Expr, all would be fine because it could use the Expr to access children. But it does not know about it, it is just stored in one and all it knows about are things that either were captured when the closure was created or whatever arguments you give to it. (When you look in your own mailbox, you don't get to look in your neighbour's mailbox too, unless they have given you the key.)

What would make more sense is to implement a function on Expr that gets the values and then calls the function with them, allowing you to write a more simple closure as @kmdreko commented. The function could look something like this:

impl Expr {
    pub fn do_the_thing(self) -> Option<f64> {
        let left = (*self.children.0)?.do_the_thing()?;
        let right = (*self.children.1)?.do_the_thing()?;
        Some((self.func)(left, right))
    }
}

This would then allow you to simplify the implementation of Add:

impl Add<Expr> for Expr {
    type Output = Expr;

    fn add(self, rhs: Expr) -> Self::Output {
        let children = (Box::new(Some(self)), Box::new(Some(rhs)));
        Expr {
            children,
            func: Box::new(|l, r| l + r),
        }
    }
}
Johann150
  • 735
  • 1
  • 7
  • 15