Propagating higher-kinded-lifetimes? implementation of `Predicate` not general enough

Viewed 72

I'm trying to write a Rust library that lets users define and compose predicates for certain kinds of data. This is all easy when the predicates act on owned data, but it's turning out to be harder when they act on borrowed data. I think it's easier to understand my problem by reading an annotated code example (I'm sorry I couldn't make it shorter, but I think everything in it is required to understand my situation).

// First, some of the types that I'm interested in writing predicates for. MyValue and
// MyParsedBorrowedValue
struct MyValue {
    a: String
}

struct MyParsedBorrowedValue<'a> {
    _name: &'a str,
    _last_name: &'a str
}

fn parse_value(value: &MyValue) -> MyParsedBorrowedValue<'_> {
    // (imagine some parsing that borrows from MyValue)
    MyParsedBorrowedValue {
        _name: &value.a,
        _last_name: &value.a
    }
}


// Now, imagine I have this trait

trait Predicate<T> {
    fn passes(&self, t: &T) -> bool;
}

// I can write a generic combinator like:

struct Not<T, P: Predicate<T>> {
    p: P,
    _p: std::marker::PhantomData<T>
}

impl<T, P: Predicate<T>> Not<T, P> {
  fn new(p: P) -> Not<T, P> { Not { p, _p: std::marker::PhantomData }}
}

impl<T, P: Predicate<T>> Predicate<T> for Not<T, P> {
    fn passes(&self, t: &T) -> bool {
        !self.p.passes(t)
    }
}

// I can write predicates on MyValue. For example:

struct IsMyValueGood {}

impl Predicate<MyValue> for IsMyValueGood {
    fn passes(&self, _t: &MyValue) -> bool {
        // some processing
        true
    }
}

// I can also write predicates on MyBorrowedParsedValue

struct IsMyParsedBorrowedValueGood {}

impl<'a> Predicate<MyParsedBorrowedValue<'a>> for IsMyParsedBorrowedValueGood {
    fn passes(&self, _t: &MyParsedBorrowedValue<'a>) -> bool {
        // some processing ...
        false
    }
}

// Finally, and this is the most important bit: I want a function that would take a
// Predicate on a "MyParsedBorrowedValue" and transform it into a predicate for a
// MyValue. Something like:

fn map_predicates(p: impl for<'a> Predicate<MyParsedBorrowedValue<'a>>)
    -> impl Predicate<MyValue> {

  // I believe this is a good use of higher-kinded lifetimes, though I'm not very
  // familiar with them.
  struct PredicateMapper<P: for<'a> Predicate<MyParsedBorrowedValue<'a>>> {
      p: P
  }

  impl<P: for<'a> Predicate<MyParsedBorrowedValue<'a>>> Predicate<MyValue> for PredicateMapper<P> {
      fn passes(&self, value: &MyValue) -> bool {
          self.p.passes(&parse_value(value))
      }
  }

  PredicateMapper { p }
}

// This is all good but sadly the following does not compile:

fn compose_predicates() {
    // This compiles:
    let complex_predicate_1 = map_predicates(IsMyParsedBorrowedValueGood{});

    // This doesn't compile!
    // Error: implementation of `Predicate` is not general enough
    let complex_predicate_2 = map_predicates(Not::new(
        IsMyParsedBorrowedValueGood {}
    ));
}

Link to compiler explorer https://godbolt.org/z/oonfcMdK3

I understand that the Not combinator is not propagating the for <'a> outside, which is what map_predicate needs to do its job. However, I don't know how I would get Not to do that, given that its definition doesn't involve any lifetimes. I guess I could make a custom NotMyParsedBorrowedValue that handles the lifetimes in the correct way, but I would prefer having a single Not combinator that works for all kinds of predicates. Is this possible?

0 Answers
Related