Returning wrapped iterator from RwLocked container

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) -> &U is not a FnOnce(&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 FnOnce bound
     // 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,)>,
    
  • 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

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.