简体   繁体   中英

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>`

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.

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

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

Thanks for you answers. Actually I misunderstood the Box<> concept and moreover I think I don't reason well.., In Java for my project I would have made interfaces inheritance (about function type like monomial, polynomial, differentiable...;) and implemented them as records (and some default methods). I want my objects be immutable. I've read that in rust thinking about inheritance is mostly mistaking so I'm trying to test some approaches but it always leads me to nasty code so I think I'd better learn about rust's design patterns...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM