Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I provide a default Debug implementation?

It is considered good practice to #[derive(Debug)] for most structs you create to aid in debugging. However, this is not possible if your struct contains a type without Debug, such as traits. But if the trait is under my control, is there something I can do to let users' implementations of said trait show up in the debug message?

I could require that people who implement my trait also implement Debug, but I don't like having to add that arbitrary requirement:

trait MyTrait: Debug { ... }

I could just implement Debug for my trait:

trait MyTrait { ... }

impl Debug for MyTrait {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "MyTrait {{ ... }}")
    }
}

This doesn't allow implementations to override Debug - it's almost as if the function is not virtual. How can I make this work?

use std::fmt;
use std::fmt::{ Formatter, Debug };

#[derive(Debug)]
struct A {
    a: Box<Data>,
}

trait Data {}

impl Debug for Data {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Data{{ ... }}")
    }
}

#[derive(Debug)]
struct B(i32);

impl Data for B {}

fn main() {
    let a = A{ a: Box::new(B(42)) };
    println!("{:?}", a);
}

Outputs:

A { a: Data{ ... } }

What I want:

A { a: B(42) }

I only want the first output when B does not implement Debug.

like image 719
Justin Avatar asked Feb 19 '17 23:02

Justin


People also ask

How do I enable debugging in Rust?

To enable debug info, set debug = true in your config. toml. Setting debug = true turns on many different debug options (e.g., debug-assertions , debug-logging , etc.)

What is derive debug in Rust?

#[...] is an attribute on struct Person . derive(Debug) asks the compiler to auto-generate a suitable implementation of the Debug trait, which provides the result of {:?} in something like format!(


1 Answers

You can create your own trait method. Types that wish to have enhanced debugging and implement Debug can delegate:

use std::fmt;
use std::fmt::{ Formatter, Debug };

#[derive(Debug)]
struct Container(Box<Data>);

trait Data {
    fn debug_fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Data {{ ... }}")
    }
}

impl Debug for Data {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.debug_fmt(f)
    }
}

#[derive(Debug)]
struct Overrides(i32);

impl Data for Overrides {
    fn debug_fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.fmt(f)
    }
}

#[derive(Debug)]
struct Defaults(i32);
impl Data for Defaults {}

fn main() {
    let a = Container(Box::new(Overrides(42)));
    println!("{:?}", a);
    let a = Container(Box::new(Defaults(42)));
    println!("{:?}", a);
}

An alternate solution that requires the unstable specialization feature:

#![feature(specialization)]

use std::fmt;
use std::fmt::{Formatter, Debug};

struct Container<D>(Box<D>) where D: Data;

impl<D> Debug for Container<D>
    where D: Data
{
    default fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Container(Data {{ ... }})")
    }
}

impl<D> Debug for Container<D>
    where D: Data + Debug
{
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Container({:?})", self.0)
    }
}

trait Data {}

#[derive(Debug)]
struct Overrides(i32);
impl Data for Overrides {}

struct Defaults(i32);
impl Data for Defaults {}

fn main() {
    let a = Container(Box::new(Overrides(42)));
    println!("{:?}", a);
    let a = Container(Box::new(Defaults(42)));
    println!("{:?}", a);
}

Note that this places the burden on the container.

like image 194
Shepmaster Avatar answered Sep 28 '22 07:09

Shepmaster