Lifetime issue when storing closure in struct

Hi,

I am creating a Validator library, which let me create a validator, pass the getter function pointer and a checker function pointer to setup rules, then use the validator object to validate different targets.

pub struct Validator<TTarget>
{
    rules: Vec<Box<dyn Fn(&TTarget) -> bool>>,
}

impl<TTarget> Validator<TTarget> {
    pub fn add_rule<TValue>(
        &mut self,
        getter: fn(&TTarget) -> &TValue,
        checker: fn(&TValue) -> bool,
    ) where
        TValue: ?Sized,
    {
        let ptr = move |target: &TTarget| {
            let value = getter(target);
            checker(value)
        };
        self.rules.push(Box::new(ptr));
    }

    pub fn validate(&self, target: &TTarget) -> bool {
        !self.rules.iter().any(|r| !r(target))
    }
}

Attempt 1: Rust Playground
On my first attempt, it won't compile. I don't understand why there's a requirement for TTarget and TValue to live as long as the trait object rules?

Attempt 2: Rust Playground
I introduced 'a lifetime to satisfy the requirement. It works fine for targets with 'static lifetime, however for a struct with lifetime like this, it is asking me to make the target to live longer than the validator. I am not sure why?

I feel like Rust thinks the Validator object holds reference to TTarget, while it's not?

Could someone explain what's going on and guide me to the right direction? Thank you.

Hello, I am fairly new to rust :slight_smile:

Attempt 1:
I've played with your code and figured out that the referance to String address you have defined goes out of scope at line 53. Thus it's not useful anymore for the rest of the execution. You can overcome this by leaking your object.

You can leak rules like this:

(You need to introduce lifetime to your Validator struct)

let mut rules = Box::leak(Box::new(vec![]));
let mut validator = Validator { rules: &mut rules };

This should also compile. However I would do something like this and avoid using referance if possible:

Sort of, but not quite. In general, if a type (TTarget) has a lifetime in it, then another type which contains — mentions — that type (dyn Fn(&TTarget)) must also “live” no longer than that type.

There are no actual or hypothetical references existing here — this is purely about the region over which you are allowed to hold values of that type. References are just a specific concrete case of a type with a lifetime in them, and the rules are designed so that anything you can possibly do with references under those rules is sound. But sometimes the rules reject things which we can imagine would work anyway.

In your code, you have a Validator that works on MyData — but MyData is actually a generic type, generic over lifetimes. So you can never actually have a Validator<MyData>; only a Validator<MyData<'a>>, for some specific lifetime 'a. Therefore, the validator may not outlive the data with lifetime 'a.

Your second playground will compile if you move the Validator to be created after the address string. This is not actually because the order they're created matters, but rather the order in which they are dropped — deallocated, which is always the reverse of the order variables were declared, and must be an order in which no (potential) invalid references result. That is, validator must be dropped before address. (Inserting calls to drop() will not help, because the borrow checker doesn't know that guarantees a drop as opposed to sending the value off somewhere else.)

I don't know of a way to write the code you want that will work. There probably is one, likely involving some custom traits, but I'm not thinking of one at the moment.

Great explanation thank you, at least I understand why it does not work!

Wonderful, I just learned something new here about Box::leak!
I think it will leak the memory for the vector though.
I might have to drop support for MyData<'a> and just use MyData like you said.
Thank you so much!