If not specialization, you need negative implementations and reasoning about them in coherence, which enables other things we don't have yet, like mutually exclusive traits.
I don't think your playground is possible via blanket implementation, even with incomplete features (but I could be wrong). Perhaps use a custom trait instead. Incidentally, if you do use incomplete_features, don't be surprised if you encounter compiler bugs and future breakage.[1]
I've found some kind of recursion, meaning that A<N> implements From<A<{N - 1}>>. I think it should be a way to have the expected behavior with a similar system, but when I try something like From<A<{N + X}>> I get a recursion limit error (even if X is hardcoded, like 2 for example.
I think the problem is not totally unsolvable, but the solution may be long to compile and/or poorly optimized.
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
#[derive(Copy, Clone, Debug)]
struct A<const N: usize>([u64; N]);
struct Condition<const U: bool>;
trait IsTrue {}
impl IsTrue for Condition<true> {}
impl From<A<0>> for A<1>
{
fn from(_v: A::<0>) -> Self {
Self([0; 1])
}
}
impl<const N: usize> From<A<N>> for A<{N + 1}>
where
Condition<{N != 0}>: IsTrue,
A<N>: From<A<{N - 1}>>
{
fn from(v: A::<N>) -> Self {
let mut out = [0; N+1];
for i in 0..N {
out[i] = v.0[i]
}
Self(out)
}
}
fn main() {
let _a: A<2> = A([1]).into();
let _b: A<5> = <A<3> as Into<A<{2+1+1}>>>::into(A([1;3])).into();
}
The idea of a separate trait is good and seems to work overall:
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
#[derive(Copy, Clone, Debug)]
struct A<const N: usize>([u64; N]);
trait Cast<T> {
fn cast(self) -> T;
}
struct Condition<const U: bool>;
trait IsTrue {}
impl IsTrue for Condition<true> {}
impl<const N: usize, const M: usize> Cast<A<N>> for A<M>
where
Condition<{N > M}>: IsTrue,
{
fn cast(self) -> A<N> {
let mut out = [0; N];
for i in 0..M {
out[i] = self.0[i];
}
A(out)
}
}
fn main() {
let a = A([1]);
println!("{a:?}");
let b: A::<2> = a.cast();
println!("{b:?}");
let c: A::<3> = a.cast();
println!("{c:?}");
}
But! Only when everything is in a single compilation unit. If the implementation is inside lib, and the method call happens in another crate (for example, integration tests), then the call does not compile, resulting in an error:
the following trait bounds were not satisfied:
Moreover, it doesn't work specifically for generic_const_exprs: if you replace
impl<const N: usize, const M: usize> Cast<A<N>> for A<M>
where
Condition<{N > M}>: IsTrue,
{
...
}
with
impl<const N: usize, const M: usize> Cast<A<N>> for A<M>
where
Condition<true>: IsTrue,
{
...
}
all works, albeit without restrictions.
It seems that this trick is not very transparent between crate compilation.