Flo Flo
Flo Flo

Reputation: 31

Can't solve lifetime problem in simple method

I'm brand new at Rust, coming from Java and I experience some difficulties with owenership and lifetimes. I'd like to do some formal calculus but obviously I'm not doing things the right way... Can you please show me why?

In my code I define those:

pub trait Function {
    fn differentiate(&self) -> Box<dyn Function>;
}

pub struct Add<'a>(&'a Box<dyn Function>, &'a Box<dyn Function>);

impl<'a> Function for Add<'a> {
    fn differentiate(&self) -> Box<dyn Function> {
        let x = self.0.differentiate();
        let y = self.1.differentiate();
        let add = Add(&x, &y);
        Box::new(add)
    }

Compiler tells me I have a borrowing problem with x and y, I understand why but can't figure out how to solve it; I tried to set let x: Box<dyn Function + 'a> = ... but then I got lifetime problems on defining add and on the last line:

expected `Box<(dyn Function + 'static)>`
              found `Box<dyn Function>`

Upvotes: 0

Views: 129

Answers (3)

Ahmed Masud
Ahmed Masud

Reputation: 22374

The simplest is to remove the references, because your Box is a smart pointer. So:

pub trait Function {
    fn differentiate(&self) -> Box<dyn Function>;
}

pub struct Add(Box<dyn Function>, Box<dyn Function>);

impl Function for Add {
    fn differentiate(&self) -> Box<dyn Function> {
        let x = self.0.differentiate();
        let y = self.1.differentiate();
        Box::new(Add(x, y))
    }
}

I think what you are trying to return a type after adding two types that implement differentiation.

There are a few different ways to think about this... Here's one:

pub trait Differentiable {
    type Result;
    fn differentiate(self) -> Self::Result;
}

pub struct Add<OP1, OP2>(OP1, OP2);

impl<OP1, OP2> Differentiable for Add<OP1, OP2>
where
    OP1: Differentiable,
    OP2: Differentiable,
{
    type Result = Add<OP1::Result, OP2::Result>;

    fn differentiate(self) -> Self::Result {
        let x = self.0.differentiate();
        let y = self.1.differentiate();
        Add(x, y)
    }
}

Upvotes: 0

jthulhu
jthulhu

Reputation: 8657

An alternative design would be to require differentiable functions to be clonable (because you'll probably want to be able to use them in different places), and avoid dynamic dispatch (and the indirection required by trait objects) altogether. Here is the implementation of two simple operations as an example.

trait Differentiable: Clone {
    type Output;
    fn differentiate(&self) -> Self::Output;
}

#[derive(Clone)]
struct Add<L, R>(L, R);

impl<L: Differentiable, R: Differentiable> Differentiable for Add<L, R> {
    type Output = Add<L::Output, R::Output>;
    
    fn differentiate(&self) -> Self::Output {
        Add(self.0.differentiate(), self.1.differentiate())
    }
}

#[derive(Clone)]
struct Mul<L, R>(L, R);

impl<L: Differentiable, R: Differentiable> Differentiable for Mul<L, R> {
    type Output = Add<Mul<L::Output, R>, Mul<L, R::Output>>;
    
    fn differentiate(&self) -> Self::Output {
        Add(Mul(self.0.differentiate(), self.1.clone()), Mul(self.0.clone(), self.1.differentiate()))
    }
}

Note that this easily allows adding useful constraints, such as making them callable (if you actually want to be able to evaluate them) or stuff like that. These, alongside with the identify function and the constant function should probably be enough for you to "create" polynomial calculus.

Upvotes: 1

Finomnis
Finomnis

Reputation: 22446

You cannot return an object that references local variables.

This is nothing special to Rust, it is like that in every language that has references (Java doesn't, in Java everything is a reference counting smart pointer). Writing this in C/C++ would be undefined behaviour. The borrow checker is here to prevent undefined behaviour, so it rightfully complains.

Here is a wild guess of what you might have wanted to do.

I'm unsure why you use references here, so I removed them. Your code looks like Add should own its members.

pub trait Function {
    fn differentiate(&self) -> Box<dyn Function>;
}

pub struct Add(Box<dyn Function>, Box<dyn Function>);

impl Function for Add {
    fn differentiate(&self) -> Box<dyn Function> {
        let x = self.0.differentiate();
        let y = self.1.differentiate();
        let add = Add(x, y);
        Box::new(add)
    }
}

Upvotes: 1

Related Questions