Want to store structs as field but getting lifetime errors

Hi there,

I have a piece of rust code whereby I iteratively call a function repeatedly, iteration shown below.

impl MainStruct {
    fn iteration(
        &mut self,
        x: &Vec<f64>,
        y: &Vec<f64>,
    ) -> Vec<f64> {
        // Generate a
        let parameters = parameter_function();
        let a = A::default(
            parameters
        );

        a.update(x, y);

        // Define the closure
        let closure = move |x: &[f64]| {
            let res = a.method_of_a(&x.to_vec());
            res.0 - res.1
        };

        // Create b struct
        let b = B::new(&closure);

        // Create c struct
        let c = C::new(vec![b]);

        // Initialise D struct
        let mut d = D::new();

        // Get res
        let res = function(&c, &mut d);

        // do stuff

}
}

In each iteration I am initialising 4 structures, A, B, C and D, but A and D are initialised the same for every iteration. I have tried to make a and d fields of my main struct, and make them initialised when MainStruct is initialised, however in doing so I get lifetime errors, stating that the lifetime of &mut self is lifetime 1 and that 'let b = B::new(&closure);' cast requires 1 must outlive static.

impl MainStruct {
    fn iteration(
        &mut self,
        x: &Vec<f64>,
        y: &Vec<f64>,
    ) -> Vec<f64> {
        self.a.update(x, y);

        // Define the closure
        let closure = move |x: &[f64]| {
            let res = self.a.method_of_a(&x.to_vec());
            res.0 - res.1
        };

        // Create b struct
        let b = B::new(&closure);

        // Create c struct
        let c = C::new(vec![b]);

        // Get res
        let res = function(&c, &mut self.d);

        // do stuff

}
}

any ideas on how to fix this issue? (side note, I am coming from a python background, so if my approach is not the "rust way", please let me know)

thanks

This question provides incomplete information. Judging by your error description

the function signature of B::new seems quite relevant, or whatever else the source of the 'static constraint is. Additionally, a full compiler error is always very appreciated, especially for cases like this where you don’t provide a fully reproducible code example. You paraphrase the error, but it’s always possible that the full error message contains useful details that are being missed this way.

The next step then is that we’ll need to figure out whether that constraint is necessary or not, which determines whether the fix needs to happen inside this function, or maybe in the signature of B::new and/or elsewhere. I’d question the necessity given that you work with a (short lived) reference to the closure in the first place.

3 Likes

this error indicates the definition of your B::new<F>(f: F) must have a F: 'static bounds, but your closure captures self.a, which is behind of mutable borrow with some lifetime (let's call it 'a as in &'a mut self), so the closure also contains a lifetime of 'a.

depending on the situation, there's two direction to fix this, either:
a) remove the 'static bounds for the parameter type of B::new(), or if the implementation requires 'static, the only options left is
b) don't capture self.a in the closure, you can either make a clone of a or just revert back to the original code

regarding whether the code is idiomatic, it's hard to say for sure from the reduced code snippets without knowing what the code actually meant to work. but just an minor observation: the parameter type for x and y can probably use slices instead of references of Vec (unless of course you need to call certain Vec specific methods).

@steffahn @nerditation thanks for your quick replies.

The struct B has the following code

/// Define Type.
type MyClosure = dyn Fn(&[f64]) -> f64;

/// Define B.
pub struct B<'a> {
    pub handler: &'a MyClosure,
}

impl<'a> B<'a> {
    /// Instantiates a new `B`.
    pub fn new(handler: &'a MyClosure) -> Self {
        B {
            handler: handler,
        }
    }
}

I'm guessing from both of your replies this issue is from B having 'static bounds?

You’re running into the subtleties of lifetime elision for trait object types.

If you had written e.g.

/// Define B.
pub struct B<'a> {
    pub handler: &'a dyn Fn(&[f64]) -> f64,
}

impl<'a> B<'a> {
    /// Instantiates a new `B`.
    pub fn new(handler: &'a MyClosure) -> Self {
        B {
            handler: handler,
        }
    }
}

the results might have been less compilation errors. (Also, you probably also want to remove the move from the closure eventually, which I suppose was added due to compiler suggestions stemming from the 'static constraint, too).'

While the above may seem identical to your code it’s not.

type MyClosure = dyn Fn(&[f64]) -> f64;

has an elided trait object lifetime and stands for

type MyClosure = dyn Fn(&[f64]) -> f64 + 'static;

whereas the same trait-object-lifetime elision rules make

&'a dyn Fn(&[f64]) -> f64

stand for

&'a (dyn Fn(&[f64]) -> f64 + 'a)

which is less restrictive than

&'a (dyn Fn(&[f64]) -> f64 + 'static)

In order to keep the type synonym, you could do this instead:

/// Define Type.
type MyClosure<'a> = dyn Fn(&[f64]) -> f64 + 'a;

/// Define B.
pub struct B<'a> {
    pub handler: &'a MyClosure<'a>,
}
7 Likes

@steffahn thanks very much for your reply, this got the code working just as I wanted!! many thanks!!

1 Like

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.