Because you want to capture the input lifetime, but Rust has no generic type constructors, either. Returning a reference at least captures the common case of field projection.
Potentially derailing exploration
Given a locked T, we're going to be dealing with &T (and interior mutability if needed), since we're talking about a concurrent data structure. And the staple use-case of projection is to go from a struct to a field of that struct. So that means we're going to be going from &T -> &U.
However, in this case, we want to go not a field, but to something constructed from a field (or &T). We want &T -> Values<'_, K, V>. This time, that is -- maybe some other time we'd want &T -> Keys<'_, K, V>.
How do you write a trait bound for a closure that takes a &T and hands back a lifetime-capturing, but otherwise generic, type?
impl<'a, R: RawRwLock + 'a, T: ?Sized + 'a> RwLockReadGuard<'a, R, T> {
pub fn map<U: ?Sized, F>(s: Self, f: F) -> MappedRwLockReadGuard<'a, R, U>
where
F: FnOnce(&T) -> ????????,
// Goal: Support &U, Keys<'_, K, V>, Values<'_, K, V>, etc
There's currently no direct way to do so on stable.
- You can't use a type parameter, because that must resolve to a single type
- And types that vary by lifetime are not types, they are type constructors (including
&U) - So a
FnOnce(&T) -> &Uis not aFnOnce(&T) -> V
// Won't work, `U` can't capture the input lifetime pub fn map<U: ?Sized, F>(s: Self, f: F) -> MappedRwLockReadGuard<'a, R, *U> where F: FnOnce(&T) -> U,- And stable Rust doesn't let you omit the return type in a
FnOncebound
// Won't work due to using the `FnOnce<(&'_ T,)>` syntax // And there's probably difficulty in pulling out the correct return // type in this particular case (it stores a `*const U` currently) pub fn map<F>(s: Self, f: F) -> MappedRwLockReadGuard<'a, R, <F as FnOnce<(&'??? T,)>>::Output> where for<'any> F: FnOnce<(&'any T,)>, - And types that vary by lifetime are not types, they are type constructors (including
- Rust has no generic type constructors
// Won't work, no such thing as a `U<*>` pub fn map<U<'_>, F>(s: Self, f: F) -> MappedRwLockReadGuard<'a, R, U<'*>> where F: FnOnce(&T) -> U<'_>,// Won't work, not supported and no way to name the // opaque return type to boot pub fn map<F>(s: Self, f: F) -> MappedRwLockReadGuard<'a, R, ???> where F: FnOnce(&T) -> impl Sized + '_, - There's no generic way to erase lifetimes, like there is for references
- Relevant to this particular example since it stores
*const U
- Relevant to this particular example since it stores
You can use helper traits to emulate FnOnce<(&'_ T,)> on stable, but it tends to wreck inference. And probably also doesn't address the lifetime-erasing aspect of this particular example.