Help a complicated `HRBT` problem

I have this code in playground.
And I got this error:

   Compiling playground v0.0.1 (/playground)
error: implementation of `GroupbyApply` is not general enough
   --> src/lib.rs:142:5
    |
142 | /     GroupbyUnOrdered
143 | |         ::from_vec(&a_vec)
144 | |         .apply(
145 | |             &b_vec[..],
146 | |             |x| x[0] + 1f32
147 | |         );
    | |_________^ implementation of `GroupbyApply` is not general enough
    |
    = note: `GroupbyOrdered<usize>` must implement `GroupbyApply<&'0 Vec<f32>, &[f32]>`, for any lifetime `'0`...
    = note: ...but it actually implements `GroupbyApply<&'1 Vec<f32>, &[f32]>`, for some specific lifetime `'1`

I know it's problem about lifetime bounding, how can I make it right?

1 Like

I've tried many ways, but all of it end no where. Can unsafe block help it, should I use unsafe block?

This compiles, but panics. I'm not sure if it fits your needs. I basically removed the abstraction over &[T] and Vec<T>.

1 Like

The GroupbyApply for GroupbyOrdered can be fixed by having functionality at the single lifetime level...

pub trait SliceIndexGat<'a, Guard = &'a Self> {
    type Output;
    fn slice_index_gat(&'a self, range: std::ops::Range<usize>) -> Self::Output;
}

pub trait SliceIndex: for<'a> SliceIndexGat<'a> {
    fn slice_index(&self, range: std::ops::Range<usize>) -> <Self as SliceIndexGat<'_>>::Output;
}

impl<S: for<'a> SliceIndexGat<'a>> SliceIndex for S {
    #[inline]
    fn slice_index(&self, range: std::ops::Range<usize>) -> <Self as SliceIndexGat<'_>>::Output {
        self.slice_index_gat(range)
    }
}

...and then working at a single level in the implementation:

impl<'a, I, T, E> GroupbyApply<&'a I, E> for GroupbyOrdered<T>
where
    I: ?Sized + SliceIndexGat<'a, Output = E> + 'a,
{
    fn apply<F, N>(&self, data: &'a I, f: F) -> Vec<N>
    where
        F: Fn(E) -> N,
    {
        self.index
            .iter()
            .map(|x| {
                let data_part = data.slice_index_gat(x.clone());
                f(data_part)
            })
            .collect_vec()
    }
}

(partial fix)

But the GroupbyApply for GroupbyUnOrdered is trickier because you do actually need a HRTB as written, and that's causing problems for you here:

impl<'a, I, E, T, I2> GroupbyApply<&'a I, E> for GroupbyUnOrdered<T>
where
    I: UniqueForTuple<Output = I2> + ?Sized,
    GroupbyOrdered<T>: for<'b> GroupbyApply<&'b I2, E>,
    //                 ^^^^^^^                      ^

Because E might be different based on 'b, but you're requiring that E is the same for all &'b I2.


Based on the implementations, it feels like the parameters on GroupByApply are meant to be type constructors, like GATs. I tried a couple approaches that were stymied by E being a type parameter. Even if they had worked, though, they were pretty ugly and I'd be surprised if they didn't confuse inference. So I'm going to stop poking at that for now.

If you only need a fixed set of types or type constructors, giving up on being as general as possible with traits may be a better way to go. For example if the definition of some GAT is always a reference, just use references.

If you don't know why it helps you and can't explain why it's sound, the answer to this question is no.

5 Likes

@baiguoname Can you please explain why you chose this answer as the solution? Did you manage to solve your problem? If yes, would you mind sharing it? Because this is the most astonishingly difficult HRTB problem I have ever seen.

1 Like

No, I didn't solve the problem. I chose that answer as a solution because I thought it might be the closest solution I could see.
I think the way of organizing traits in the original playground makes things complicated, maybe it does not follow the code style of rust. I thought I should make trait methods as simple as possible, then they should take as few arguments as possible. At the same time, let the generic type be in the implemented struct.
Then I rewrote the code in this playground, and I actually found it makes things simple. Then I could use the trait in this chain way:

data
    .transact_time
    .to_groupby_ordered()
    .get_group_data(&trade_money)
    .apply(|x| x.sum())
    .collect_vec()

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.