2017-02-19 2 views
8

La plupart des structures que vous créez pour faciliter le débogage sont considérées comme une bonne pratique à #[derive(Debug)]. Cependant, cela n'est pas possible si votre structure contient un type sans Debug, tel que des traits. Mais si le trait est sous mon contrôle, y a-t-il quelque chose que je puisse faire pour permettre aux utilisateurs de mettre en œuvre ce trait dans le message de débogage?Comment fournir une implémentation de débogage par défaut?

je pourrais exiger que les personnes qui mettent en œuvre mon trait également à mettre en œuvre Debug, mais je n'aime pas avoir à ajouter cette exigence arbitraire:

trait MyTrait: Debug { ... } 

je pouvais mettre en œuvre Debug pour mon trait:

trait MyTrait { ... } 

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

Cela ne permet pas aux implémentations de surcharger Debug - c'est presque comme si la fonction n'était pas virtuelle. Comment puis-je faire ce travail?

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); 
} 

Sorties:

A { a: Data{ ... } } 

Ce que je veux:

A { a: B(42) } 

Je veux que la première sortie lorsque B n'implémente pas Debug.

+1

Sons comme [Permettre à un trait d'implémenter son trait de parent] (https://github.com/rust-lang/rfcs/issues/1024) est ce que vous cherchez après un implic par défaut de 'Debug' pour votre trait . – Lukazoid

Répondre

5

Vous pouvez créer votre propre méthode de trait. Les types qui souhaitent avoir amélioré le débogage et mettre en œuvre Debug peut déléguer:

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); 
} 

Une solution alternative qui nécessite la fonction de spécialisation instable:

#![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); 
} 

Notez que cela impose le fardeau de la récipient.