Implementing Display trait for a trait object

trait Hisbullah {}
impl Hisbullah for str {}   
impl Hisbullah for usize {}
impl Hisbullah for String {}
impl fmt::Display for Box<dyn Hisbullah> {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "{}", *self)
        }
}

Output Error:
thread 'main' has overflowed its stack

The overflow occurs exactly when I call the print function.
Q) What am I doing here?
Well I have a Vec<T> where I don't know what the T could be. So I am using Box<dyn Hisbullah> like we are taught. I later need to print these T too so I am implementing the fmt::Display Trait. I am sort of using Hisbullah as a guard here. If I don't impl Hisbullah for a primitive data type, that type won't be accepted.

Q) Could I not use generics instead of trait objects?
Yes I can, given the vector instance will be homogeneous but lets say that might not always be the case.

Q) Can you not use Display as supertrait?
I can. It's a failsafe option. I want to learn this way here.

You are implementing the Display trait with… well, itself. So Display::fmt() just calls itself, causing infinite recursion.

Well,

  • You're implementing Display for Box<dyn Hisbullah>
  • The fmt function takes &self, so, it's a &Box<dyn Hisbullah>
  • You then try to format *self with a {}
  • That means calling the Display::fmt for Box<dyn Hisbullah>
  • Which is the function you're in...
  • So you have unconditional recursion here and blow your stack

You have to link up the printabiilty of your T to the Box<dyn Hisbullah> somehow. What if I wrote:

struct DoesNotImplementDisplayOrDebugEtc;
impl Hisbullah for DoesNotImplementDisplayOrDebugEtc;
let dnidode = DoesNotImplementDisplayOrDebugEtc;
let bdh: Box<dyn Hisbullah> = Box::new(dnidode);
println!("{}", bdh);
// ^^^^^^^^^^^^^^^ what function does this ultimately call?

The Display supertrait would be the most direct way.

2 Likes

I was trying to hit something like this:
Display - Rust By Example (rust-lang.org)

Yes. Supertraiting Display works fine.

  • I wanted to learn implementing a std Trait like Display which should be easy. I got assured of the possibility by this: Display - Rust By Example (rust-lang.org).
  • You've put it best. I need to link the printability of T with Box<dyn Hisbullah> Isn't dyn Hisbullah supposed to mean an 'object' that implements Hisbullah? The right way I got to 'unBox' was using the dereferncing operator *. Can't I ask the Display implementation to use the display implementation of the now-out-of-the-box T ?

Yes, but that doesn't mean it implements Display as well. When you cast Box<usize> (for instance) to Box<dyn Hisbullah>, you erase the knowledge that it was once a Box<usize>, and you can only use those things which are part of trait Hisbullah.

Correct, but you didn't do it enough: as quinedot mentioned, inside the fmt method, self is &Box<dyn Hisbullah>, so * gives you still a Box<dyn Hisbullah>. If you wanted to get at the value in the box, you'd need to write **self. (But that won't compile, since dyn Hisbullah doesn't implement Display, among other reasons.)

No, because the value in the box is not known to the compiler to be of type T; its type has been erased and now you can only use behavior that is part of dyn Hisbullah. dyn Hisbullah does not implement Display.

2 Likes

I almost getting there. But here's the problem.
I always wanted to implement it for Hisbullah. The compiler asked me to use a Box. The book said so too (Chapter 19 of the main book, IIRC) that I should either use Box<dyn > or & would & help?

You explained it all wonderfully but it looks like I am being directed to just impl Display for Hisbullah but the compiler won't let me. I can rewrite the code for usize, u8 (like you said, the knowledge of being a primitive is gone) as I expect it to be trivial. This is exactly the genesis of this endeavour. But none of it is possible if I can't get the Box out of the header. Anyway I can do that?

The Box appears to be irrelevant here; you would have the same problem implementing Display for dyn Hisbullah directly:

impl fmt::Display for dyn Hisbullah + '_ {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        /* but what do you put here? */
    }
}

If you have a type like MinMax you can implement Display by deferring to what's inside it. But dyn Hisbullah is opaque, because that's the whole point of it: it could be anything that implements Hisbullah, even if that thing doesn't implement Display. So the only behavior you can rely on inside the impl is behavior guaranteed by Hisbullah itself.

If it's not logical for a type to implement Hisbullah but not Display, well, then just make Display a supertrait, which directly solves this problem.

2 Likes

I get it. Had I used a generic instead of a trait object it would have worked right?
Anyway I am using Display as a supertrait and it's working totally fine. Thank you for explaining it so well.
Thank you ALL.

The way dyn Trait works is that you have a pointer to both your original object and a vtable for dynamic dispatch of the trait and any supertraits. So if you want to call any methods on the underlying type, they have to be part of the trait or a supertrait, so that they are in the vtable.

But if U implements Display, &U and Box<U> already implement Display too. Which means that if Display is a supertrait of Trait, since dyn Trait implements Display, so does &dyn Trait and Box<dyn Trait>.

So (a) there's no reason to implement it yourself, but also (b) you'll get a "conflicting implementation" error if you do. (If you need to customize the implementation, you wrap it up in a new type.)

If you don't have Display as a supertrait, there is no conflict and you can implement Display on dyn Trait (or Box<dyn Trait>). But you can't utilize the implementations of the underlying types (because they're not in the vtable). So you could only print out "yep, this is a dyn Hisbullah alright" or something. So I think the compiler error led you a bit astray here -- what you really do seem to want is the supertrait.

1 Like

What do you mean by that, specifically? Unless you know (and tell the compiler) that a type is Display, you can't rely on it being Display. I.e., the following doesn't work for the same reason:

impl<T: Hisbullah> Display for T {
    fn fmt(&self, f: &mut Formatter) {
        write!(f, "{}", self)
    }
}

(There are other reasons why this wouldn't work; basically, you are not allowed to implement a 3rd-party crate for all types, but that's a completely different question.)

What you are really trying to express here is in fact a supertrait relationship (as others have already mentioned). Trying to do anything else is unnecessary complication, and probably not going to work.

1 Like

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.