Struggling with HRTBs (again) (as usual) (we're not even doing anything weird this time)

We just have this:

pub type Predicate = dyn (for<'x, 'de, 'a> Fn(
    &'x (dyn 'a + erased_serde::Deserializer<'de>)
) -> bool) + Send + Sync;

And it makes a bunch of errors:

error: implementation of `FnOnce` is not general enough
  --> tests/basic_match.rs:10:31
   |
10 |     let preds = vec![("dict", Box::new(|v| -> bool { todo!() }) as Box<datafu::Predicate>)].into_iter().collect();
   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(&'2 dyn erased_serde::de::Deserializer<'_>) -> bool` must implement `FnOnce<(&'1 (dyn for<'de> erased_serde::de::Deserializer<'de> + 'a),)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(&'2 dyn erased_serde::de::Deserializer<'_>,)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
  --> tests/basic_match.rs:10:31
   |
10 |     let preds = vec![("dict", Box::new(|v| -> bool { todo!() }) as Box<datafu::Predicate>)].into_iter().collect();
   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(&(dyn erased_serde::de::Deserializer<'_> + '2)) -> bool` must implement `FnOnce<(&'x (dyn for<'de> erased_serde::de::Deserializer<'de> + '1),)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(&(dyn erased_serde::de::Deserializer<'_> + '2),)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
  --> tests/basic_match.rs:10:31
   |
10 |     let preds = vec![("dict", Box::new(|v| -> bool { todo!() }) as Box<datafu::Predicate>)].into_iter().collect();
   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(&dyn erased_serde::de::Deserializer<'2>) -> bool` must implement `FnOnce<(&'x (dyn for<'de> erased_serde::de::Deserializer<'de> + 'a),)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(&dyn erased_serde::de::Deserializer<'2>,)>`, for some specific lifetime `'2`

(Also what? This isn't FnOnce...)

…lol, rustc generously providing a separate, almost identical, error message for each of the generic lifetimes…


That’s type inference of closures (that are generic over lifetimes) for you. It only really works well if it knows a trait bound directly on the closure. So something like

fn pred<P>(f: P) -> P
where
    P: for<'x, 'de, 'a> Fn(&'x (dyn 'a + erased_serde::Deserializer<'de>)) -> bool + Send + Sync,
{
    f
}

help, so you can write

vec![("dict", Box::new(pred(|v| -> bool { todo!() })) as Box<Predicate>)]

Alternatively, making the closure arguments type signature explicit enough so all generic lifetime become visible will also do the job in this case, but it’s hopelessly verbose…

|v: &(dyn '_ + erased_serde::Deserializer<'_>)| -> bool { todo!() }
// instead of
pred(|v| -> bool { todo!() })
2 Likes

why are closures so bad...

literally every time we use them we run into issues... maybe we should be using traits instead... this works for now tho, thanks!

any idea why it does that btw? this feels like something that could be improved...

it gets worse

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/pattern.rs:48:52
   |
10 |     ("str", datafu::pred(|v| { String::deserialize(v).is_ok() })),
   |                                                    ^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined here...
  --> src/pattern.rs:48:26
   |
10 |     ("str", datafu::pred(|v| { String::deserialize(v).is_ok() })),
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that the reference type `&mut dyn erased_serde::de::Deserializer<'_>` does not outlive the data it points at
  --> src/pattern.rs:48:32
   |
10 |     ("str", datafu::pred(|v| { String::deserialize(v).is_ok() })),
   |                                ^^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the anonymous lifetime #3 defined here...
  --> src/pattern.rs:48:26
   |
10 |     ("str", datafu::pred(|v| { String::deserialize(v).is_ok() })),
   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that the types are compatible
  --> src/pattern.rs:48:32
   |
10 |     ("str", datafu::pred(|v| { String::deserialize(v).is_ok() })),
   |                                ^^^^^^^^^^^^^^^^^^^
   = note: expected `<&mut dyn erased_serde::de::Deserializer<'_> as Deserializer<'_>>`
              found `<&mut dyn erased_serde::de::Deserializer<'_> as Deserializer<'_>>`

@steffahn any ideas?

ngl we wish closures were deprecated. they never work.

okay so the solution is apparently to just... not use for<>. at all:

/// A predicate.
pub type Predicate = dyn (Fn(
    &mut dyn erased_serde::Deserializer<>
) -> bool) + Send + Sync;

/// Helper to build predicates because HRTB inference is the worst.
pub fn pred<F>(f: F) -> Box<Predicate>
where
    F: (Fn(
        &mut dyn erased_serde::Deserializer<>
    ) -> bool) +  Send + Sync + 'static,
{
    Box::new(f)
}

literally wasted a week on this because closures are bad and nobody told us to try removing things.