Does `move` closure copy a trait object `F: Fn(f64) -> f64 + Copy`

pub fn triangle(a: f64, b: f64, s: f64) -> impl Fn(f64) -> f64 {
    move |x| {
        if (a - s) <= x && x <= (a + s) {
            return b * (1.0 - (x - a).abs() / s);
        }
        0.0
    }
}

fn minf<F: Fn(f64) -> f64 + Copy>(mf: F, input: f64) -> impl Fn(f64) -> f64 {
    move |x: f64| -> f64 { input.min((mf)(x)) }
}

pub struct FuzzySet<F: Fn(f64) -> f64> {
    pub universe: Vec<f64>, 
    pub membership_f: F,   
}

impl<F: Fn(f64) -> f64> FuzzySet<F> {
    pub fn new(universe: &Vec<f64>, fuzzy_f: F) -> Self {
        FuzzySet {
            universe: universe.clone(),
            membership_f: fuzzy_f,
        }
    }

    // the part we are interested on
    pub fn min<'a>(&'a self, input: f64) -> FuzzySet<impl Fn(f64) -> f64 + 'a> {
        FuzzySet::new(&self.universe, minf(&self.membership_f, input))
    }
}

From the above code, why is min need to have explicit lifetime annotation 'a when minf should return a function that has a copy of self.membership_f not its reference, or am I not understanding some things?

In minf(), the returned Fn(f64) -> f64 type lives no longer than F (by its nature, since it needs to capture an F). When you call minf(&self.membership_f, input) with a reference, minf()'s F is equivalent to min()'s &'self F. Therefore, the returned closure can't live any longer than 'self, since it doesn't store a copy of self.membership_f, but instead a temporary reference. To fix this, the most trivial way would be to require F: Copy or F: Clone in FuzzySet.

In min you're calling mif passing &self.membership_f, which has type &'a F. This ends up the F in minf, which is captured in the returned impl Fn(f64) -> f64 by minf. Yes, minf is moving it inside the closure, but if what you're moving is a reference you'll need a lifetime anyway.

To solve this you need to pass self.membership_f to minf, but to do this you'll need to add a Copy bound on the F of the impl.

For anyone who stumbles on this thread, and can't figure out how to implement something similar to this. Below is the fixed code. The main takeaway is function can return a closure that guarantees some trait using the parentheses ( ) like (impl Fn(f64) -> f64 + Copy), idk that this is possible before :man_facepalming:

pub fn triangle(a: f64, b: f64, s: f64) -> (impl Fn(f64) -> f64 + Copy) {
    move |x| {
        if (a - s) <= x && x <= (a + s) {
            return b * (1.0 - (x - a).abs() / s);
        }
        0.0
    }
}

fn minf<F: Fn(f64) -> f64 + Copy>(mf: F, input: f64) -> (impl Fn(f64) -> f64 + Copy) {
    move |x: f64| -> f64 { input.min((mf)(x)) }
}


pub struct FuzzySet<F: Fn(f64) -> f64 + Copy> {
    pub universe: Vec<f64>, 
    pub membership_f: F,    
}

impl<F: Fn(f64) -> f64 + Copy> FuzzySet<F> {
    pub fn new(universe: &Vec<f64>, fuzzy_f: F) -> Self {
        FuzzySet {
            universe: universe.clone(),
            membership_f: fuzzy_f,
        }
    }
    pub fn min(&self, input: f64) -> FuzzySet<impl Fn(f64) -> f64 + Copy> {
        FuzzySet::new(&self.universe, minf(self.membership_f, input))
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.