Using const within dynamic traits

Is it possible to use a const within a trait that will be used with dyn? This code errors out by saying that the trait 'TestTrait' cannot be made into an object.

Isn't its size known since the MYCONST has a constant size of usize?

trait TestTrait {
    const MYCONST: usize;

    fn test(&self) -> usize;
}

struct Test;

impl TestTrait for Test {
    const MYCONST: usize = 1;
    
    fn test(&self) -> usize {
        Self::MYCONST
    }
}

// ... other structs that implement TestTrait ...

fn main() {
    let mut tests = Vec::<Box<dyn TestTrait>>::with_capacity(10);
    let test = Box::new(Test{});
    tests.push(test);
}
error[E0038]: the trait `TestTrait` cannot be made into an object
  --> src/main.rs:20:21
   |
20 |     let mut tests = Vec::<Box<dyn TestTrait>>::with_capacity(10);
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^ `TestTrait` cannot be made into an object

I don't know why, but currently associated constants do not fit into the notion of object safety. See here: Traits - The Rust Reference. The reference does not contain a reason for this, maybe it has something to do with the vtable not containing entries for associated constants? I really don't know.

A compile-time constant coming from a run-time dynamic type doesn't make sense, I don't think it is at all possible. Even if you stuff the appropriate associated values into the vtable, they will cease being evaluatable at compile time.

Furthermore, and perhaps even more fundamentally, something that is associated with the type can't be summoned from thin air when the type is dynamic. For example, the following should be valid, since dyn TestTrait implements TestTrait:

fn main() {
    dbg!(<dyn TestTrait>::MYCONST);
}

However, there's nowhere to get the value of MYCONST from! dyn TestTrait may have any number of underlying concrete types, and the code doesn't say anything about which type's associated const it should forward to, without the existence of a value (i.e., purely at the type level).

2 Likes

I wonder if traits with a default value for associated constants would ever become object safe. I.e.:

trait TestTrait {
    const MYCONST: usize = 1;

    fn test(&self) -> usize;
}

assert_eq!(<dyn TestTrait>::MYCONST, 1);

I'm not quite sure I fully grasp your argument here. Wouldn't we evaluate the constants at compile time for the concrete types that implement TestTrait and create a vtable at runtime pointing to it, much like with a trait method?

That's not what I'm talking about. I'm talking about retrieving the value. If that needs to go through a vtable (because it depends on a runtime condition), then it can't be retrieved at compile time, so it can't be a const.

I'm afraid that's not any more meaningful. Default values are syntactic sugar for not having to explicitly implement a particular trait item for a specific type. Conceptually, the associated item is still associated with a concrete type. The default associated const would mean "if some impl for type T doesn't spell out the value of this const, then automatically implement it with the value of 1". It wouldn't mean "assume a value of 1 at runtime/projection time if the type is ambiguous for some reason". It's a help for the implementor, not a universal crutch for situations where the type is unknown.

2 Likes

I could imagine something like

trait Foo {
    #[bikeshed(bar)]
    const BAR: u32;
}

generating in some final form method

    const fn bar(&self) -> u32 { <Self as Foo>::BAR }

and also as an implementation detail just stashing the value in the vtable for dyn Foo.

But you need the &self to find the vtable.

1 Like

Here's an approximation of my last comment that you can use on stable.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.