Search code examples
rusttraitsassociated-types

Trait associated const is not available in trait definition context despite appropriate trait bound


Here's what I'm working on (playground):

pub trait DisplayWidth {
    const DISPLAY_WIDTH: usize;

    fn chunks<'a>(s: &'a str) -> Chunks<'a, Self> {
        Chunks(s.chars(), PhantomData)
    }
}

pub struct Chunks<'a, T: ?Sized>(std::str::Chars<'a>, PhantomData<T>);

impl<'a, T: DisplayWidth> Iterator for Chunks<'a, T> {
    // 4 bytes in a max-width char
    type Item = SmallString<[u8; 4 * T::DISPLAY_WIDTH]>;

    fn next(&mut self) -> Option<Self::Item> {
        let mut s = SmallString::new();
        for _ in 0..T::DISPLAY_WIDTH {
            s.push(self.0.next()?);
        }
        Some(s)
    }
}

This generates an E0599, but only for the type Item definition:

error[E0599]: no associated item named `DISPLAY_WIDTH` found for type parameter `T` in the current scope
  --> src/geometry/tile.rs:23:41
   |
23 |     type Item = SmallString<[u8; 4 * T::DISPLAY_WIDTH]>;
   |                                         ^^^^^^^^^^^^^ associated item not found in `T`
   |
   = help: items from traits can only be used if the type parameter is bounded by the trait

Of interest is the fact that rustc doesn't complain at all about the use of the associated const in the for loop; just at the associated type definition.

My suspicion is that this is a compiler bug: given that T is constrained by DisplayWidth, it should be available in the trait associated type definition context, but just isn't. However, I'm asking here before filing a bug report against rustc because I'm not 100% certain of that.

The workaround I'm using for the moment is simple: define a separate const and use that:

pub const CHUNK_WIDTH: usize = 4;
    type Item = SmallString<[u8; 4 * CHUNK_WIDTH]>;

Is there a solution which would allow using DisplayWidth::DISPLAY_WIDTH, though?


Solution

  • This is a limitation of array length; it can't use any parameters or generic bounds even if they are scope. The issue came up during the stabilization of associated consts and the decision taken was to stabilize them without it.

    This limitation is specific to array length parameters, everything else works:

    pub trait Shape {
        const SIDES: usize;
    }
    
    pub trait SimpleIter {
        const Item: usize;    
    }
    
    impl<T: Shape> SimpleIter for T {
        // this is fine
        const Item: usize = T::SIDES;
    }
    

    The issue can being tracked here and here