#[derive (Debug)] not playing well with "dyn"

#[derive (Debug) is not playing well with "dyn".

"Generally speaking, you should just derive a Debug implementation." - Rust manual. This fails badly around "dyn" traits. Messages include

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/lib.rs:38:11
   |
38 | #[derive (Debug)]           
   |           ^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 39:19...
  --> src/lib.rs:39:19
   |
39 | pub struct Region<'a> {
   |                   ^^
note: ...so that the types are compatible
  --> src/lib.rs:38:11
   |
38 | #[derive (Debug)]           
   |           ^^^^^
   = note: expected `Region<'a>`
              found `Region<'_>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
  --> src/lib.rs:40:5
   |
40 |     viewables: HashMap<u32, &'a mut dyn Viewable>,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Debug`
              found `Debug`
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

Playground.

It's puzzling that this is a problem. All "Debug" should need is a non-mutable borrow. What needs a static lifetime? With all the #[Derive (Debug)} removed, this compiles.

I've read this help item but it doesn't seem to apply directly.

use std::collections::HashMap;

pub trait Viewable {}

impl dyn Viewable {}

// Can't auto-generate Debug for dyn traits.
use core::fmt::Debug;
impl Debug for dyn Viewable {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "UNIMPLEMENTED")                 // ***NEED TO CALL DEBUG FN FOR TRAIT OBJECT***
        }
    }

#[derive (Debug)]
pub struct ViewableObject {
    pub viewable: dyn Viewable,           
}

impl ViewableObject {}

#[derive (Debug)]
struct Primitive {}

#[derive (Debug)]
struct Avatar {}

impl Viewable for Primitive {}  
impl Viewable for Avatar {}


#[derive (Debug)]    
pub struct RegionLocInfo {
    pub name:  String,          // name of region
}


#[derive (Debug)]           
pub struct Region<'a> {
    viewables: HashMap<u32, &'a mut dyn Viewable>,
    tiles: Vec<Tile<'a>>
}

impl<'a> Region<'a> {
////    pub fn cleanup(&mut self) {} // prepare for deletion
    
}

/// Part of a region, with its static objects
#[derive (Debug)]
pub struct Tile <'a> {
    viewables: HashMap<u32, &'a mut dyn Viewable>
}
 
impl<'a> Tile<'a> {}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/lib.rs:38:11
   |
38 | #[derive (Debug)]           
   |           ^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 39:19...
  --> src/lib.rs:39:19
   |
39 | pub struct Region<'a> {
   |                   ^^
note: ...so that the types are compatible
  --> src/lib.rs:38:11
   |
38 | #[derive (Debug)]           
   |           ^^^^^
   = note: expected `Region<'a>`
              found `Region<'_>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
  --> src/lib.rs:40:5
   |
40 |     viewables: HashMap<u32, &'a mut dyn Viewable>,
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Debug`
              found `Debug`
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/lib.rs:50:11
   |
50 | #[derive (Debug)]
   |           ^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 51:18...
  --> src/lib.rs:51:18
   |
51 | pub struct Tile <'a> {
   |                  ^^
note: ...so that the types are compatible
  --> src/lib.rs:50:11
   |
50 | #[derive (Debug)]
   |           ^^^^^
   = note: expected `Tile<'a>`
              found `Tile<'_>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
  --> src/lib.rs:52:5
   |
52 |     viewables: HashMap<u32, &'a mut dyn Viewable>
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Debug`
              found `Debug`
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0495`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Instead of impl Debug for dyn Viewable, you can require anything that implements Viewable to also implement Debug; the trait object will then implement Debug as well:

pub trait Viewable: Debug {}

(Playground)


Alternately, you can implement it manually by providing the hidden lifetime parameter for trait objects in your implementation:

impl<'a> Debug for dyn Viewable+'a {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "UNIMPLEMENTED")                 // ***NEED TO CALL DEBUG FN FOR TRAIT OBJECT***
        }
    }

(Playground)

1 Like

Thanks for the quick reply. The error message indicates the derive macro is creating the problem, but that's not it. It's in my code, and thus easily fixed.

This makes me think there should be a clippy lint or something like that against impl [OtherTrait for] dyn Trait {}: nowadays people should use an explicitly named lifetime, or the '_ elided lifetime:

  • Either: impl [OtherTrait for] dyn Trait + 'static

  • Or: impl [OtherTrait for] dyn Trait + '_

    • which is sugar for @2e71828's suggestion:

      impl<'lt> [OtherTrait for] dyn Trait + 'lt


Not completely related here, but you may sometimes avoid errors by using &'a mut (dyn Viewable + 'static) rather than &'a mut dyn Viewable, which is sugar for &'a mut (dyn Viewable + 'a). The one with 'static is not as general, but precisely thanks to that, you can get access to more functionality.

For instance, one can .downcast_ref() a &'a (dyn Any + 'static), but not a &'a dyn Any.

3 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.