Skip to content

Conversation

@tarcieri
Copy link
Member

These were extracted from the module-lattice crate (which in turn was extracted from ml-dsa).

They were contributed by @bifurcation and seem generally useful. I have extracted them largely verbatim except cleaning up a few things which were triggering lint failures.

Having them as traits instead of inherent methods is nice because you can bound on them, where the bounds they actually require would be somewhat annoying to have downstream code repeat.

These were extracted from the `module-lattice` crate (which in turn was
extracted from `ml-dsa`).

They were contributed by @bifurcation and seem generally useful. I have
extracted them largely verbatim except cleaning up a few things which
were triggering lint failures.

Having them as traits instead of inherent methods is nice because you
can bound on them, where the bounds they actually require would be
somewhat annoying to have downstream code repeat.
@tarcieri tarcieri requested a review from newpavlov January 31, 2026 22:24
Copy link
Member

@newpavlov newpavlov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems fine to me, but the traits looks a bit weird on the first glance, especially considering that we are unlikely to have more than one impl for them.

Having them as traits instead of inherent methods is nice because you can bound on them, where the bounds they actually require would be somewhat annoying to have downstream code repeat.

So you effectively want trait aliases, right? Maybe we could introduce inherent methods and use the traits just as pseudo-aliases?

@tarcieri
Copy link
Member Author

tarcieri commented Feb 1, 2026

So you effectively want trait aliases, right? Maybe we could introduce inherent methods and use the traits just as pseudo-aliases?

Yeah, something like sec1::ModulusSize perhaps.

That said, having something like that may be necessary, as I tried ditching the traits entirely and moving to inherent methods and got flatten to work, but unflatten seems a little less trivial:

error[E0207]: the type parameter `M` is not constrained by the impl trait, self type, or predicates
  --> src/flatten.rs:27:12
   |
27 | impl<T, N, M> Array<T, N>
   |            ^ unconstrained type parameter

error[E0207]: the type parameter `M` is not constrained by the impl trait, self type, or predicates
  --> src/flatten.rs:47:16
   |
47 | impl<'a, T, N, M> &'a Array<T, N>
   |                ^ unconstrained type parameter

error[E0390]: cannot define inherent `impl` for primitive types
  --> src/flatten.rs:47:1
   |
47 | / impl<'a, T, N, M> &'a Array<T, N>
48 | | where
49 | |     N: ArraySize + Div<M> + Rem<M, Output = U0>,
50 | |     M: ArraySize,
51 | |     Quot<N, M>: ArraySize,
   | |__________________________^
   |
   = help: consider using an extension trait instead

Didn't look too long, but this is what I was trying:

impl<T, N, M> Array<T, N>
where
    N: ArraySize + Div<M> + Rem<M, Output = U0>,
    M: ArraySize,
    Quot<N, M>: ArraySize,
{
    pub fn unflatten(self) -> Array<Array<T, Quot<N, M>>, M> {
        let part_size = Quot::<N, M>::USIZE;
        let whole = ManuallyDrop::new(self);

        // SAFETY: this is doing the same thing as what is done in `Array::split`.
        // Basically, this is doing transmute between [T; K*N] and [[T; K], M], which is guaranteed
        // to be safe by the Rust memory layout of these types.
        Array::from_fn(|i| unsafe {
            let offset = i.checked_mul(part_size).expect("overflow");
            ptr::read(whole.as_ptr().add(offset).cast())
        })
    }
}

I think I'll merge this as-is but not release right away if you want to take a crack at it @newpavlov.

@tarcieri tarcieri merged commit a202541 into master Feb 1, 2026
14 checks passed
@tarcieri tarcieri deleted the hybrid-array/flatten-and-unflatten branch February 1, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants