Help to share closures between structs

Hi all! =)
I'm in trouble to build a system of properties and features. In my mind a property is a function with a name (use to debug) and a feature is a truth on a property. The purpose is to build a decision tree where each choice can be printed.

fn main() {
  let john = User { name: "John".to_owned(), age: 16 };
  let not_john = User { name: "Paul".to_owned(), age: 18 };

  let prop_name = Property::new(&"name", User::name);
  let is_john = prop_name.eq("John".to_owned());

  println!("{}", (is_john.predicate)(john)); // should return true
  println!("{}", (is_john.predicate)(not_john)); // should return false

}

struct User {
  name: String,
  age: i32,
}

impl User {
  fn name(&self) -> String {
    self.name.to_owned()
  }

  fn age(&self) -> i32 {
    self.age
  }
}

struct Property<T, R> {
  name: String,
  selector: Rc<Fn(&T) -> R>,
}
impl<T, R> Property<T, R> {
  fn new<S: 'static>(name: &str, selector: S) -> Property<T, R>
    where S: Fn(&T) -> R {
      Property { name: name.to_owned(), selector: Rc::new(selector) }
  }
}

struct Feature<T> {
  description: String,
  predicate: Box<Fn(&T) -> bool>,
}

impl<T, R: Eq> Property<T, R> {
  fn eq(&self, data: R) -> Feature<T> {
    Feature {
        description: self.name.to_owned() + "== fixed data",
        predicate: Box::new(move |it: &T| (self.selector)(it) == data)
    }
  }
}

The problem occurs when i try to create the predicate closure in the fn eq(&self, data: R) -> Feature. I think that the compiler has to many lifetime to manage and cannot understand what's happen. I have tried both Box and Rc containers but they do not work. My purpose is only to reuse the property closure into the create Feature to select the element on which the feature must be activated.

Someone see any solution ?
Thank's in advance.

Box<Fn(&T) -> bool> is actually Box<Fn(&T) -> bool + 'static>, so when you try and use it in Property::eq with a shorter lifetime (lifetime of self), it doesn't work. This can be fixed in two ways.
Version 1: This way removes the unnecessary Box in Feature, and replaces it with a generic parameter. (Note: PhantomData is needed because of how Rust handles generics, if you don't understand it yet, that is fine, just treat it as necessary syntax).
Version 2: This way adds a lifetime parameter to Feature and uses that to handle the lifetime issues.

I would recommend going with Version 1, because adding lifetime parameters to structs is more advanced and can lead to more lifetime issues if you are not careful or don't understand what is going on.


Btw, it would be nice if you gave a playground link to go along with it. This way people can jump right in and see how to help.

1 Like

There’s another option, which is to clone self.selector outside the closure and move the clone in: play

1 Like

Thank's for both responses. I wish I'll read the Phantom data documentation parts and use the version version 1 of KrishnaSannasi.