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.
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 }>()))
}