Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): Add From<[T; N]> for LimitedVec #3647

Closed
wants to merge 2 commits into from
Closed

Conversation

mqxf
Copy link
Contributor

@mqxf mqxf commented Jan 9, 2024

Resolves #3641

  • There is no way to compare M <= N in trait bounds
  • static_assert!() doesnt work on const generics
  • One potential solution I think could work uses where [(); {N - M}]:, however, it needs #![feature(generic_const_expr)]. If you think this is better I can change it to that

@reviewer-or-team

@mqxf
Copy link
Contributor Author

mqxf commented Jan 9, 2024

Just threw this little test together:

#![feature(generic_const_exprs)]

#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct LimitedVec<T, const N: usize>(Vec<T>);

impl<T, const M: usize, const N: usize> From<[T; M]> for LimitedVec<T, N>
    where [(); {N - M}]:
{
    fn from(value: [T; M]) -> Self {
        Self(value.into())
    }
}

fn main() {
    let a = [1, 2, 3];
    let b: LimitedVec<_, 5> = a.into();
    let c: LimitedVec<_, 3> = a.into();
    //let d: LimitedVec<_, 2> = a.into();
}

Where if the line is uncommented, compilation fails with this error:

error[E0080]: evaluation of `<LimitedVec<!0, 2> as std::convert::From<[!0; 3]>>::{constant#0}` failed
 --> src/main.rs:7:17
  |
7 |     where [(); {N - M}]:
  |                 ^^^^^ attempt to compute `2_usize - 3_usize`, which would overflow

I personally think this would be better than runtime assertion, but I do not know whether I can use this feature since it is techincally unstable, though should not arise any problems in this scenario.

@mqxf mqxf requested a review from techraed January 9, 2024 12:38
@thecaralice
Copy link
Contributor

Another option is using #![feature(associated_const_equality)] instead, which is, while technically still unstable, not as unstable as #![feature(generic_const_exprs)] is.
This is what I got while tinkering with this issue:

#![feature(associated_const_equality)]
use core::marker::PhantomData;

pub struct LimitedVec<T, E, const N: usize>(Vec<T>, PhantomData<E>);

macro_rules! const_conditions {
    ($($name:ident = |$($arg:ident: $argtype:ty),*| $result:expr;)*) => {
        $(
            enum $name<$(const $arg: $argtype),*> {}

            impl<$(const $arg: $argtype),*> $crate::ConstCondition for $name<$($arg),*> {
                const RESULT: bool = $result;
            }
        )*
    }
}

trait ConstCondition {
    const RESULT: bool;
}

const_conditions! {
    LessThan = |L: usize, R: usize| L < R;
}

impl<T, E, const M: usize, const N: usize> From<[T; M]> for LimitedVec<T, E, N>
where
    LessThan<N, M>: ConstCondition<RESULT = false>,
{
    fn from(value: [T; M]) -> Self {
        Self(value.into(), PhantomData)
    }
}

Or, without any macros,

#![feature(associated_const_equality)]

trait ConstCondition {
    const RESULT: bool;
}

enum LessThan<const L: usize, const R: usize> {}
impl<const L: usize, const R: usize> ConstCondition for LessThan<L, R> {
    const RESULT: bool = L < R;
}

impl<T, E, const M: usize, const N: usize> From<[T; M]> for LimitedVec<T, E, N>
where
    LessThan<N, M>: ConstCondition<RESULT = false>,
{
    fn from(value: [T; M]) -> Self {
        Self(value.into(), PhantomData)
    }
}

@mqxf
Copy link
Contributor Author

mqxf commented Jan 9, 2024

That would probably be a better solution.

@thecaralice
Copy link
Contributor

thecaralice commented Jan 9, 2024

Note that there is a drawback: you can not just #[cfg] it out like in #3470, because the compiler will still complain about ACE usage (playground), unlike GCE (playground)

@@ -37,6 +37,13 @@ use scale_info::{
#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo)]
pub struct LimitedVec<T, E, const N: usize>(Vec<T>, PhantomData<E>);

impl<T, E, const M: usize, const N: usize> From<[T; M]> for LimitedVec<T, E, N> {
Copy link
Member

Choose a reason for hiding this comment

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

I'm against of assertions in runtime for such a type, that's mainly used in non-test code. It's not worth adding potentially not checked to be unreachable by tests panic in the trait impl that's needed only for tests.
If there's no other stable solution, then it seems better to close the PR and wait for stable.

Copy link
Member

Choose a reason for hiding this comment

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

+1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no way to use static assertions without unstable features. static_assert!() doesnt work with const generics and everything else is gated behind unstable features. Unless it is fine to use them, which you have specified it isn't, then I'm going to close this PR until some feature becomes stable.

@StackOverflowExcept1on
Copy link
Member

Try this: nvzqz/static-assertions#40 (comment)

Comment on lines +40 to +46
impl<T, E, const M: usize, const N: usize> From<[T; M]> for LimitedVec<T, E, N> {
fn from(value: [T; M]) -> Self {
assert!(M <= N);
Self(value.into(), Default::default())
}
}

Choose a reason for hiding this comment

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

Suggested change
impl<T, E, const M: usize, const N: usize> From<[T; M]> for LimitedVec<T, E, N> {
fn from(value: [T; M]) -> Self {
assert!(M <= N);
Self(value.into(), Default::default())
}
}
struct LessThenOrEqual<const M: usize, const N: usize> {}
impl<const M: usize, const N: usize> LessThenOrEqual<M, N> {
const ASSERTION: () = assert!(M <= N);
}
impl<T, E, const M: usize, const N: usize> From<[T; M]> for LimitedVec<T, E, N> {
fn from(value: [T; M]) -> Self {
let _ = LessThenOrEqual::<M, N>::ASSERTION;
Self(value.into(), Default::default())
}
}

Choose a reason for hiding this comment

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

please also replace places where ::from can be used

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is still runtime assertion isnt it?

Copy link
Member

Choose a reason for hiding this comment

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

this is still runtime assertion isnt it?

No

@breathx breathx added the A0-pleasereview PR is ready to be reviewed by the team label Jan 10, 2024
@mqxf
Copy link
Contributor Author

mqxf commented Jan 10, 2024

static assertions unfortunately cant work on const generics

@mqxf
Copy link
Contributor Author

mqxf commented Jan 10, 2024

Since it is not preferable to use runtime assertions, and all static assertions I am aware of are gated behind unstable features, which I shouldn't use. I am going to close this PR.

@mqxf mqxf closed this Jan 10, 2024
@mqxf mqxf deleted the ms/issue-3641 branch January 12, 2024 08:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A0-pleasereview PR is ready to be reviewed by the team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Consider From for LimitedVec
5 participants