Cornstalks
Cornstalks

Reputation: 38238

Why does Rust want Self to be Sized when implementing an associated const with an associated type?

Consider the following code:

pub trait Trait {
  type Type;
  const CONST: Self::Type;
}

impl<T> Trait for T {
  type Type = u8;
  const CONST: u8 = 42;
}

My (incorrect?) understanding of Rust is that this code should work and that all Sized types should now implement Trait and have an associated type (Type = u8) and const (CONST = 42). Unsized types shouldn't implement this trait since impl<T> implicitly assumes T to be Sized.

However, when I try to compile the code I get the error message:

error[E0277]: the size for value values of type `T` cannot be known at compilation time
 --> src/main.rs:8:3
  |
8 |   const CONST: u8 = 42;
  |   ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `T`
  = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types--sized>
  = help: consider adding a `where T: std::marker::Sized` bound
  = note: required because of the requirements on the impl of `Trait` for `T`

My questions:

Upvotes: 5

Views: 905

Answers (1)

MB-F
MB-F

Reputation: 23647

According to this GitHub issue, this appears to be a known bug that has been around at least since Rust 1.23 (longer, I suspect).

It is not clear what is causing the problem and when/if it will be fixed. There is only a rather vague hypothesis:

I'm not familiar with the compiler internals, but my hypothesis is that associated types and constants depending on a type parameter are not evaluated properly in the constant expression evaluator. In this case, it's associated types that do not reduce well: const VAL: Self::T = 5; forces Rust to do some fancy type of computation at compile time in order to type check, but there's a bug in the code for such computations.

There are a few ways to work around the issue:

  1. Specifying a concrete type in the trait:

    pub trait Trait {
        // type Type;  // no longer used
        const CONST: u8;
    }
    
  2. Opting T out of Sized:

    impl<T: ?Sized> Trait for T {
        type Type = u8;
        const CONST: u8 = 42;
    }
    
  3. Use a function instead of a constant (credit goes to @PeterHall):

    pub trait Trait {
      type Type;
      fn const_val() -> Self::Type;
    }
    
    impl<T> Trait for T {
      type Type = u8;
      fn const_val() -> Self::Type { 42 }
    }
    

Upvotes: 2

Related Questions