# Constants in dyn traits not allowed - any workaround?

**URL:** <https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380>\
**Category:** help\
**Created:** [January 21, 2025, 3:20am UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380 "2025-01-21T03:20:54Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![John\_Nagle](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/john_nagle/32/17844_2.png) [@John\_Nagle](https://users.rust-lang.org/u/John_Nagle)\
**Post date:** [January 21, 2025, 3:20am UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380/1 "2025-01-21T03:20:54Z")

</div>

I'm trying to work around a breaking change in **winit**. Winit used to just a library, but now it has a "framework", a trait that the caller must implement.

I'm stuck maintaining **rend3-hp** , a fork of non-longer-maintained **rend3**. I'm trying to keep the same API as **rend3** while adapting it to use the new **winit**. It _almost_ works. One compile error.

Here's the problem. Rend3's "framework" trait starts out like this:

```rust
pub trait App<T: 'static = ()> {
    /// The handedness of the coordinate system of the renderer.
    const HANDEDNESS: Handedness;

    fn register_logger(&mut self) {
        #[cfg(target_arch = "wasm32")]
        console_log::init().unwrap();
...

```

Each application that uses rend3-framework has to provide a value for the const `HANDEDNESS`. This worked fine until the latest "refactoring" over at **winit**. Now, **winit** has its own framework trait that has to be implemented by its user, which in this case is **rend3**.

```rust
pub trait ApplicationHandler {
    /// Emitted when new events arrive from the OS to be processed.

```

Tying them together looks like this:

```rust
/// New Winit framework usage
type AppRef<'a> = &'a mut dyn App;
impl ApplicationHandler<AppRef<'static>> for Rend3ApplicationHandler<'_, AppRef<'_>> {
   ...

```

The trouble is that "dyn App". [If a trait instantiation is **dyn** , it can't have constants.](https://doc.rust-lang.org/reference/items/associated-items.html#associated-constants-examples) So the declaration of HANDEDNESS, above, won't work.

Making HANDEDNESS a const parameter to App would work nicely. But that breaks everything that calls rend3, which means I have to change three big programs plus about eight test cases.

So if there's any trick for working around this without changing **App** , it would help. I don't think there is one, but it's worth asking.

(This is why libraries shouldn't be made into "frameworks". Frameworks want to be in charge. They don't play well with other "frameworks")

---

<div class="post-metadata">

**Author:** ![tigregalis](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/tigregalis/32/42364_2.png) [@tigregalis](https://users.rust-lang.org/u/tigregalis)\
**Post date:** [January 21, 2025, 9:01am UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380/2 "2025-01-21T09:01:39Z")

</div>

Does something like this work for your case? Create an object-safe wrapper/proxy trait.

```rs
trait NonObjectSafe {
    const VALUE: i32;
    fn some_method(&self) -> i32;
}

trait ObjectSafe {
    fn get_value(&self) -> i32;
    fn some_method(&self) -> i32;
}

impl<T: NonObjectSafe> ObjectSafe for T {
    fn get_value(&self) -> i32 {
        T::VALUE
    }

    fn some_method(&self) -> i32 {
        self.some_method()
    }
}

struct MyStruct;

impl NonObjectSafe for MyStruct {
    const VALUE: i32 = 42;
    
    fn some_method(&self) -> i32 {
        self.get_value() * 2
    }
}

fn use_as_trait_object(obj: &dyn ObjectSafe) {
    println!("Value: {}", obj.get_value());
}

fn main() {
    let obj: &dyn ObjectSafe = &MyStruct;
    use_as_trait_object(obj);
}

```

---

<div class="post-metadata">

**Author:** ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://users.rust-lang.org/u/2e71828)\
**Post date:** [January 21, 2025, 10:09am UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380/3 "2025-01-21T10:09:02Z")

</div>

> [@John\_Nagle](#):
>
> ```rust
> /// New Winit framework usage
> type AppRef<'a> = &'a mut dyn App;
> impl ApplicationHandler<AppRef<'static>> for Rend3ApplicationHandler<'_, AppRef<'_>> {
> ...
> 
> ```
> 
> The trouble is that "dyn App". [If a trait instantiation is **dyn** , it can't have constants.](https://doc.rust-lang.org/reference/items/associated-items.html#associated-constants-examples) So the declaration of HANDEDNESS, above, won't work.

I'm not familiar with `rend3`, but this looks somewhat suspect to me-- The type argument to `ApplicationHandler` is the type that gets put into user events, and I don't see how you can avoid violating the aliasing guarantees of `&'static mut` in that role. It seems more natural to store the `App` (or a reference to it) inside the type that's implementing `ApplicationHandler`

Aside from that, can I ask why you want to use `dyn` here at all? There's likely to only be one `App` type in any given program, so monomorphization bloat shouldn't be an issue. My instinct here would be to write an implementation like this, but it's entirely possible that something I don't know about is preventing this approach.

```rust
impl<UEData, RendApp> ApplicationHandler<UEData> for Rend3ApplicationHandler<RendApp>
where
    UEData: 'static,
    RendApp: App<UEData>

```

---

<div class="post-metadata">

**Author:** ![John\_Nagle](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/john_nagle/32/17844_2.png) [@John\_Nagle](https://users.rust-lang.org/u/John_Nagle)\
**Post date:** [January 22, 2025, 4:02am UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380/4 "2025-01-22T04:02:13Z")

</div>

> Aside from that, can I ask why you want to use `dyn` here at all? There's likely to only be one `App` type in any given program, so monomorphization bloat shouldn't be an issue. My instinct here would be to write an implementation like this, but it's entirely possible that something I don't know about is preventing this approach.

Because **App** is a trait. So ...

```rust
error[E0782]: expected a type, found a trait
   --> rend3-framework/src/lib.rs:336:71
    |
336 | ...r Rend3ApplicationHandler<'_, App<'_>> {
    | ^^^^^^^
    |
help: you can add the `dyn` keyword if you want a trait object
    |
336 | impl ApplicationHandler<App<'static>> for Rend3ApplicationHandler<'_, dyn App<'_>> {
    | +++

error[E0782]: expected a type, found a trait
   --> rend3-framework/src/lib.rs:336:25
    |
336 | impl ApplicationHandler<App<'static>> for Rend3ApplicationHandler<'_, A...
    | ^^^^^^^^^^^^
    |
help: you can add the `dyn` keyword if you want a trait object
    |
336 | impl ApplicationHandler<dyn App<'static>> for Rend3ApplicationHandler<'_, App<'_>> {

```

I didn't design this. I'm just trying to make it work without a breaking change for its users (its tests and three applications of my own, at least) as other crates change underneath.

---

<div class="post-metadata">

**Author:** ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://users.rust-lang.org/u/2e71828)\
**Post date:** [January 22, 2025, 6:44am UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380/5 "2025-01-22T06:44:39Z")

</div>

> [@John\_Nagle](#):
>
> Because **App** is a trait

Right. But you should be able to deal with that via a bound on a generic parameter in place of `dyn` for this case:

```rust
impl<'a, T: 'a+App> Something for Rend3ApplicationHandler<'a, T> { ... }
    ^^^^^^^^^^^^^^^

```

> [@John\_Nagle](#):
>
> I'm just trying to make it work without a breaking change for its users ... as other crates change underneath.

It is likely that this will not be 100% achievable because `rend3` includes types defined by these crates in its public API; any breaking change in their usage will therefore necessarily pass through `rend3` and break the downstream users. That's not to say that you shouldn't try to minimize the problems, of course, just that you shouldn't expect perfection here.

As a particular example, [`App::create_window`](https://docs.rs/rend3-framework/latest/rend3_framework/trait.App.html#method.create_window) can be overridden by downstream users and is defined to take an argument of type [`winit::WindowBuilder`](https://docs.rs/winit/0.26.1/winit/window/struct.WindowBuilder.html), which [no longer exists](https://docs.rs/winit/latest/x86_64-unknown-linux-gnu/winit/?search=WindowBuilder) in the most recent version of `winit`.

---

<div class="post-metadata">

**Author:** ![John\_Nagle](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/john_nagle/32/17844_2.png) [@John\_Nagle](https://users.rust-lang.org/u/John_Nagle)\
**Post date:** [January 28, 2025, 3:57am UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380/6 "2025-01-28T03:57:15Z")

</div>

> [@2e71828](#):
>
> It is likely that this will not be 100% achievable because `rend3` includes types defined by these crates in its public API; any breaking change in their usage will therefore necessarily pass through `rend3` and break the downstream users. That's not to say that you shouldn't try to minimize the problems, of course, just that you shouldn't expect perfection here.

Right. I wasn't able to fix this with zero API changes at the Rend3 API. One API change was necessary - something that was previously a trait-level constant had to be turned into a function, because the virtual function tables for a dyn trait can't dispatch a constant.

Getting the ownership and lifetimes right, while caught between two APIs defined by others, was the usual headache. [Here](https://github.com/user-attachments/files/18553094/diffs.txt) are the diffs for the changes I needed to make. Too many.

---

<div class="post-metadata">

**Author:** ![nazar-pc](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/nazar-pc/32/14929_2.png) [@nazar-pc](https://users.rust-lang.org/u/nazar-pc)\
**Post date:** [February 20, 2025, 6:11pm UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380/7 "2025-02-20T18:11:16Z")

</div>

I'm hitting a similar situation in a different context. In fact my trait itself has the metadata defined and doesn't expect anyone else to implement it, it is just "attached" to the trait.

I found a good answer to this question here: [Why doesn't Rust support trait objects with associated constants? - Stack Overflow](https://stackoverflow.com/a/77433831/3806795)

Essentially from my understanding it boils down to the fact that functions are in the vtable and constants are not. Which raises the question: can't constants be added to the vtable too?

My thinking is the following: if I can define a default function that returns the value, but no the constant with the same value, then I should also be able to define a const method that returns the same value (currently unstable feature), at which point function a constant looks surprisingly similar to a const method with no arguments 🤔

UPD: Someone already proposed that years ago: [Make associated consts object safe - language design - Rust Internals](https://internals.rust-lang.org/t/make-associated-consts-object-safe/16357?u=nazar-pc)

---

<div class="post-metadata">

**Author:** ![nazar-pc](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/nazar-pc/32/14929_2.png) [@nazar-pc](https://users.rust-lang.org/u/nazar-pc)\
**Post date:** [February 20, 2025, 6:36pm UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380/8 "2025-02-20T18:36:33Z")

</div>

[Make associated consts object safe - language design - Rust Internals](https://internals.rust-lang.org/t/make-associated-consts-object-safe/16357?u=nazar-pc) hints that the problem would also apply to const methods that do not have `&self`, which was also hinted to me by the compiler when I removed constants, but turned out that I also didn't have `&self` in methods, hence it was still not object safe:

```rust
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> crates/contracts/ab-contracts-standards/src/lib.rs:29:8
   |
26 | pub trait Fungible {
   | -------- this trait cannot be made into an object...
...
29 | fn transfer(
   | ^^^^^^^^ ...because associated function `transfer` has no `self` parameter
...
38 | fn balance(env: &Env, address: &Address) -> Result<Balance, ContractError>;
   | ^^^^^^^ ...because associated function `balance` has no `self` parameter
help: consider turning `transfer` into a method by giving it a `&self` argument
   |
29 | fn transfer(&self, 
   | ++++++
help: alternatively, consider constraining `transfer` so it does not apply to trait objects
   |
34 | ) -> Result<(), ContractError> where Self: Sized;

```

Then I thought: if I can add `Self: Sized` to methods, can I do the same with constants?

Turns out yes! But only if I use unstable incomplete feature `generic_const_items`: [Tracking issue for generic const items · Issue #113521 · rust-lang/rust · GitHub](https://github.com/rust-lang/rust/issues/113521)

It allows writing things like this, which **are** object safe:

```rust
trait T {
    const C: &[u8] where Self: Sized;
}

```

With the caveat that it requires nightly compiler and the feature itself is not even complete yet.

---

<div class="post-metadata">

**Author:** ![system](https://sea1.discourse-cdn.com/flex019/user_avatar/users.rust-lang.org/system/32/51372_2.png) [@system](https://users.rust-lang.org/u/system)\
**Post date:** [May 21, 2025, 6:37pm UTC](https://users.rust-lang.org/t/constants-in-dyn-traits-not-allowed-any-workaround/124380/9 "2025-05-21T18:37:19Z")

</div>

Closed on May 21, 2025, 6:37pm UTC

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.
