Reputation: 31
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
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
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
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