It seems like you can use Generic Associated Types to get the exact functionality that Higher Ranked Trait Bounds provide, with a little extra type-level indirection. Or without (all of) the jargon, you can use #![feature(generic_associated_types)] to be generic over using Rc or Arc, so long as you're okay with using Type<RcProvider> rather than Type<Rc>.
#![feature(never_type)]
#![feature(generic_associated_types)]
#![allow(incomplete_features)]
/*!
We want:
```not_rust
struct UsesRc< R: { Arc or Rc } > {
inner: R< u32 >,
}
```
Unfortunately, this requires higher kinds...
or does it?
GAT can be used to get this exact behavior,
if you squint a little...
*/
use std::{rc::Rc, sync::Arc, ops::Deref};
pub trait RProvider {
type R<T>: Deref<Target=T> + Clone;
}
pub struct RcProvider(!);
pub struct ArcProvider(!);
impl RProvider for RcProvider {
type R<T> = Rc<T>;
}
impl RProvider for ArcProvider {
type R<T> = Arc<T>;
}
pub struct UsesRc< R: RProvider > {
pub inner: R::R< u32 >,
}
Is there something I'm missing that makes "full" HRTB more powerful than GAT, or is this actually just HRTB-behind-type-indirection? (One downside of GAT-as-HRTB is that that the provider indirection structs have to be actively defined by the implementer of the trait for coherence to work out. Full HRTB wouldn't need the extra type, thus sidestep that extra coherence check.)
however I'm not a functional programmer at all and advanced Haskell is inpenetrable to me.
, and I'm still not pleased with the naming I have used (which I think plays an important role to better express these semantics), so there are plenty of legitimate reasons to find my code unreadable
. I'd gladly go in more detail to help people grok this generic verbiage, but I don't think IRLO is the right place for that.