Home > OS >  Checking Constant Generics for Certain Properties in Rust
Checking Constant Generics for Certain Properties in Rust

Time:10-29

In this playground, I want to implement a method only for const generic parameters for which a certain property holds: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=3e4d5f9f27912d032308a390a56f5f94

I am using a zero-sized type and add a method to it:

pub struct Resource<const N: usize> {}

impl<const N: usize> Resource<N> {
    const fn shorten<const M: usize>(self) -> Resource<M>
    where
        [(); N - M]:, // type existence only ensured if N >= M
    {
        // Runtime checks, though they should not be needed
        if M <= N {
            Resource {}
        } else {
            panic!("Resources can only be shortened")
        }
    }
}

The idea is that a Resource<N> type can be shortened to Resource<M> if N >= M.

However, when I use it like this:

pub fn bar<const N: usize>(
    resource: Resource<N>,
) -> Result<((), Resource<{ N - 1 }>), Box<dyn std::error::Error>>
where
    [(); N - 1]:,
{
    Ok(((), resource.shorten::<{ N - 1 }>()))
}

I get the following compiler error:

error: unconstrained generic constant
  --> src/main.rs:43:22
   |
43 |     Ok(((), resource.shorten::<{ N - 1 }>()))
   |                      ^^^^^^^
   |
   = help: try adding a `where` bound using this expression: `where [(); N - M]:`
note: required by a bound in `Resource::<N>::shorten`
  --> src/main.rs:8:14
   |
6  |     const fn shorten<const M: usize>(self) -> Resource<M>
   |              ------- required by a bound in this
7  |     where
8  |         [(); N - M]:, // type existence only ensured if N >= M
   |              ^^^^^ required by this bound in `Resource::<N>::shorten`

I understand (and found in other online resources) that the compiler suggestion with where might be misleading (as it is not a commonly used feature). Ignoring the suggestion, I am not sure why and where this bound is required in the first place.

In bar, the shorten call is executed on Resource<N> (clear from parameter) to return Resource<{N - 1}> (clear from turbo-fish). What am I missing?

Happy to hear some thoughts from more experienced Rustaceans.

CodePudding user response:

The compiler does not analyze the expressions (in general this is also impossible). It does verbatim replacement. If you want this code to compile, you need to replace N and M with the actual value used:

pub fn foo<const N: usize>(
    resource: Resource<N>,
) -> Result<((), Resource<{ N - 2 }>), Box<dyn std::error::Error>>
where
    [(); N - (N - 1)]:,
    [(); (N - 1) - (N - 2)]:,
{
    let (baz, resource): ((), Resource<{ N - 1 }>) = bar::<{ N }>(resource)?;
    resource.dbg();
    // new resource is N-1; returning it as N-2 should work, as it is smaller
    // 'shorten<M>'  does not exist, if there is no conversion possible

    Ok((baz, resource.shorten::<{ N - 2 }>()))
}

pub fn bar<const N: usize>(
    resource: Resource<N>,
) -> Result<((), Resource<{ N - 1 }>), Box<dyn std::error::Error>>
where
    [(); N - (N - 1)]:,
{
    Ok(((), resource.shorten::<{ N - 1 }>()))
}

Playground.

  • Related