In Salsa, there is a higher-ranked trait bound on a trait. I've seen HRTBs on function definitions but not on a trait. What does it mean?
pub trait Query: Debug + Default + Sized + for<'d> QueryDb<'d> {
...
fn query_storage<'a>(
group_storage: &'a <Self as QueryDb<'_>>::GroupStorage,
) -> &'a Arc<Self::Storage>;
}
https://github.com/salsa-rs/salsa/blob/fc6806a/src/lib.rs#L370
As in, how should I read this? Is it saying that, for any Query, there is a corresponding QueryDB that has some lifetime?
How is this different from
pub trait Query<'d>: Debug + Default + Sized + QueryDb<'d>
aside from that impls cannot specify 'd?
how should I read this?
It means that any implementation of Query also implements QueryDb<'d> for all possible values of 'd (i.e. all lifetimes) at once. Therefore, in a generic context, the trait bound T: Query implies T: for<'d> QueryDb<'d>.
How is this different from
pub trait Query<'d>: Debug + Default + Sized + QueryDb<'d>aside from that impls cannot specify
'd?
By repeating the lifetime parameter on Query, it means that all trait bounds T: Query will need to be changed to T: for<'d> Query<'d> in order to be equivalent to the version where the HRTB is in Query itself.
This is basically a workaround for the lack of generic associated types. With generic associated types, QueryDb would instead look like this:
pub trait QueryDb: Sized {
/// Dyn version of the associated trait for this query group.
type DynDb<'d>: ?Sized + Database + HasQueryGroup<Self::Group> + 'd;
/// Associate query group struct.
type Group: plumbing::QueryGroup<GroupStorage = Self::GroupStorage>;
/// Generated struct that contains storage for all queries in a group.
type GroupStorage;
}
Before the pull request that introduced this lifetime parameter, QueryDb wasn't a separate trait; its members were part of Query. The generic associated type would allow us to merge QueryDb back into Query.
After reading the comments on that pull request, I get the impression that this change didn't yield the expected results. The goal was to allow a bound different from the implied 'static on associated type DynDb, but since every Query implements QueryDb<'d> for all possible 'd, that means every Query implements QueryDb<'static>. Therefore, in all implementations of QueryDb, the DynDb cannot possibly borrow anything with a lifetime shorter than 'static, otherwise the implementation of Query wouldn't be allowed (the bound for<'d> QueryDb<'d> wouldn't be satisfied).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With