Search code examples
genericsrustconst-generics

Why is Y, a constrained const generic, reported as unconstrained here?


I’m writing a fixed-size bit sequence type in Rust with the nightly features generic_const_exprs and int_roundings, and I’m able to impl BitAndAssign as such (I’ve not included the body because it isn’t necessary to verify that the first example builds and the second one doesn’t):

trait Bits {
    const BITS: u32;
}
impl Bits for u32 {
    const BITS: u32 = Self::BITS;
}

const fn bslen(x: u32, b: u32) -> usize {
    x.div_ceil(b) as usize
}

struct BitSet<const X: u32, T: Bits = u32>
where
    [(); bslen(X, T::BITS)]:
{
    data: [T; bslen(X, T::BITS)]
}

use std::ops::BitAndAssign;

impl<const X: u32, const Y: u32, T: Bits>
    BitAndAssign<&BitSet<Y, T>>
for BitSet<X, T>
where
    [(); bslen(X, T::BITS)]:,
    [(); bslen(Y, T::BITS)]:
{
    fn bitand_assign(&mut self, other: &BitSet<Y, T>) {}
}

But when I try to change it to use Borrow<BitSet<Y, T>> instead of &BitSet<Y, T>, like so:

use std::ops::BitAndAssign;
use std::borrow::Borrow;

impl<const X: u32, const Y: u32, T: Bits, BSY: Borrow<BitSet<Y, T>>>
    BitAndAssign<BSY>
for BitSet<X, T>
where
    [(); bslen(X, T::BITS)]:,
    [(); bslen(Y, T::BITS)]:
{
    fn bitand_assign(&mut self, other: BSY) {}
}

I get an error:

error[E0207]: the const parameter `Y` is not constrained by the impl trait, self type, or predicates
  --> src/minrep.rs:22:26
   |
22 | impl<const X: u32, const Y: u32, T: Bits, BSY: Borrow<BitSet<Y, T>>>
   |                          ^ unconstrained const parameter
   |

I’m not sure what this means, since Y is constrained, or why it only happens in the second case. generic_const_exprs is unstable, so this could just be a compiler bug, but I figured I should ask just in case there’s something obvious, or not so obvious, that I’m missing.


Solution

  • One BSY type could implement Borrow<BitSet<Y, T>> for multiple Ys. In this case, knowing that BitSet<X, T> implements BitAndAssign<BSY> is simply not enough for the compiler to infer the value of Y as there are multiple options.

    This is why the compiler complains that Y is unconstrained -- given the type BitSet<X, T> and its implementation of the BitAndAssign<BSY> trait (for a particular type BSY), the compiler cannot necessarily determine the value of Y. On the other hand, given the type BitSet<X, T> and its implementation of the BitAndAssign<&BitSet<Y, T>> trait, the compiler knows exactly what value of Y to use as it is present in the trait definition.