I am new to Rust, and am working on a project as a learning experience. In the project I want to be able to do the following:
fn main() {
let a = FnType::new(|x| x + 1);
let b = FnType::new(|x| x + 10);
let c = a + b;
let d = c + a + a + c;
print!("Result: {}", c.call(100));
print!("Result: {}", d.call(100));
}
I want to be able to compose functions using +. Here is my likely naive attempt at implementing this:
The main thing is to be able to implement Copy for FnType, as this is what would allow the clean API, but this is not possible unless I have a reference to Fn as Copy cannot be implemented for Fn.
In the code above the FnType::new function does not work because it says that f does not live long enough.
What do I need to do so that I can implement this with a clean API, being able to utilize Copy on a newtype for Fn?
Thanks for the correction, I don't know where I got this from. The docs explain it really well.
Anyway, to solve your real problem, I think it's best to replace &static Fn(usize) -> usize with a function pointer fn(usize) -> usize, which are Copy.
As the add implementation involves a capturing closure, it’s hard to propose anything “better” than Box<dyn Fn…> for the inner type. Any form of Boxing does hover automatically preclude Copy. Perhaps you’re fine with only having Clone though. For trait objects, this can be achieved e.g. via the crate dyn_clone - Rust.
Here’s some example code
/*
[dependencies]
dyn-clone = "1"
*/
use std::ops::Add;
use dyn_clone::{DynClone, clone_trait_object};
trait FnTypeTrait: Fn(usize) -> usize + DynClone {}
impl<T: Fn(usize) -> usize + Clone> FnTypeTrait for T {}
clone_trait_object!(FnTypeTrait);
fn main() {
let a = &FnType::new(|x| x + 1);
let b = &FnType::new(|x| x + 10);
let c = &(a + b);
let d = &(c + a + a + c);
println!("Result: {}", c.call(100));
println!("Result: {}", d.call(100));
}
#[derive(Clone)]
pub struct FnType(Box<dyn FnTypeTrait>);
impl FnType {
pub fn new(f: impl Fn(usize) -> usize + 'static + Clone) -> FnType {
FnType(Box::new(f))
}
pub fn call(&self, u: usize) -> usize {
(self.0)(u)
}
}
impl Add for FnType {
fn add(self, other: FnType) -> FnType {
FnType::new(move |x| (other.0)((self.0)(x)))
}
type Output = FnType;
}
impl Add<&FnType> for FnType {
fn add(self, other: &FnType) -> FnType {
let other = other.clone();
FnType::new(move |x| (other.0)((self.0)(x)))
}
type Output = FnType;
}
impl Add<FnType> for &FnType {
fn add(self, other: FnType) -> FnType {
let this = self.clone();
FnType::new(move |x| (other.0)((this.0)(x)))
}
type Output = FnType;
}
impl Add<&FnType> for &FnType {
fn add(self, other: &FnType) -> FnType {
let this = self.clone();
let other = other.clone();
FnType::new(move |x| (other.0)((this.0)(x)))
}
type Output = FnType;
}
This is perfect! I had the same idea of making the operands to + references, but I could not figure out how to implement Add in a way in which they would chain.