I'm stuck on a problem with naming lifetimes.
I have the following trait:
pub trait Matcher {
type ActualT: Debug + ?Sized;
fn matches(&self, actual: &Self::ActualT) -> bool;
}
I have implemented this for a struct called IsEncodedStringMatcher
. This struct makes reference to another nested Matcher
implementation:
struct IsEncodedStringMatcher<ActualT, InnerMatcherT> {
inner: InnerMatcherT,
phantom: PhantomData<ActualT>,
}
The nested Matcher
must match against string slices, which are constructed in the matches
implementation:
fn matches(&self, actual: &Self::ActualT) -> bool {
std::str::from_utf8(actual.as_ref()).map(|s| self.inner.matches(&s)).unwrap_or(false)
}
To implement this, there must be a trait bound on InnerMatcherT
specifying that it implements Matcher
and that it's associated ActualT
is a string slice. I must then specify a lifetime for that string slice.
impl<ActualT, InnerMatcherT> Matcher for IsEncodedStringMatcher<ActualT, InnerMatcherT>
where
ActualT: AsRef<[u8]>,
InnerMatcherT: Matcher<ActualT = &'actual str>,
// ^^^^^^^ What goes here?
{...}
But that lifetime must actually come from the parameter actual
to the matches
method, which I can't reference from the impl's block trait bounds. So I don't know how to name that lifetime.
Thus far I've tried:
-
Using the
for <'actual>
construct. But then one can't use the matcher, since any candidate inner matcher will be bound to a specific lifetime forActualT
. -
Adding the lifetime parameter as a parameter to the struct itself. But then the lifetime on the struct has no relationship to that of the
actual
parameter on thematches
method. -
Adding a lifetime parameter to the
Matcher
trait itself. But this makes the lifetimes too rigid. Other parts of the same library create local variables and run the inner matcher against those.For example:
struct DisplayMatcher<ActualT, InnerMatcherT> { inner: InnerMatcherT, phantom: PhantomData<ActualT>, } impl<'a, ActualT: Debug + Display + 'a, InnerMatcherT> Matcher<'a> for DisplayMatcher<ActualT, InnerMatcherT> where InnerMatcherT: Matcher<'a, ActualT = String>, { type ActualT = ActualT; fn matches(&self, actual: &'a Self::ActualT) -> MatcherResult { self.inner.matches(&format!("{actual}")) } }
This fails with:
error[E0716]: temporary value dropped while borrowed --> src/lib.rs:28:29 | 28 | self.inner.matches(&format!("{actual}")) | --------------------^^^^^^^^^^^^^^^^^^^- | | | | | creates a temporary value which is freed while still in use | argument requires that borrow lasts for `'a` 29 | } | - temporary value is freed at the end of this statement |
This feels like the kind of problem generic associated types were intended to solve, but I don't see how to use them to solve this.
Any guidance would be appreciated. Thanks!
Please see also a playground.