How to store functions in structs

Hi there,

I need to store custom functions (which are parsed from a string) in a struct.
My first attempt was to do something like this:

pub struct Flightpath {
    pub func: Box<dyn Fn(f64) -> f64>,
}

That the struct Stroke is used in RoundScore, which is used in Scorecard. It worked fine until now, that I have to copy the RoundScore struct.

pub fn get_player_score(&self, player: &Player, round: i64) -> RoundScore {
    .
    .
    .
    if let Some(score) = player_card.scored.get(&round) {
        return *score; // do I have to clone here?
    } else {
        return RoundScore::new();
    }
}

That does not work, because Copy is not implemented for Flightpath and thus RoundScore. Rust is fairly new to me and I feel a bit lost, since I have no idea how to go about this.

PS:
I need the functions to render areas within those functions, or flight paths described by a function.

One thing you can do is to change it to this:

pub struct Flightpath {
    pub func: fn(f64) -> f64,
}

This may or may not work depending on what functions you put inside it. Only some functions can be stored with an fn(f64) -> f64. If that doesn't work, you can change the Box to Arc, which makes it possible to clone the function.

use std::sync::Arc;

#[derive(Clone)]
pub struct Flightpath {
    pub func: Arc<dyn Fn(f64) -> f64>,
}

However if you take this route, you must use score.clone() rather than *score because cloning an Arc requires incrementing a counter, since an Arc keeps track of how many clones of it there are.

2 Likes

You can also use the fact that closures can be Clone, depending what they capture.

That could look something like this:

pub struct Flightpath {
    pub func: Box<dyn CloneFn>,
}

impl Clone for Flightpath {
    fn clone(&self) -> Self {
        Self {
            func: self.func.clone_box(),
        }
    }
}

pub trait CloneFn: Fn(f64) -> f64 {
    fn clone_box(&self) -> Box<dyn CloneFn>;
}

impl<F: Clone + 'static> CloneFn for F
where
    F: Fn(f64) -> f64,
{
    fn clone_box(&self) -> Box<dyn CloneFn> {
        Box::new(self.clone())
    }
}

fn main() {
    let extra = 3.0;
    let fp1 = Flightpath {
        func: Box::new(move |x| x + extra),
    };
    let fp2 = fp1.clone();
    assert_eq!((fp2.func)(1.0), 4.0);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=dd786038155ff51e1e6a270d55b231d3

1 Like