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