2017-02-19 33 views
8

Uważa się, że dobrą praktyką jest #[derive(Debug)] dla większości struktur tworzonych w celu ułatwienia debugowania. Nie jest to jednak możliwe, jeśli twoja struktura zawiera typ bez Debug, taki jak cechy. Ale jeśli cecha jest pod moją kontrolą, czy jest coś, co mogę zrobić, aby umożliwić użytkownikom implementację tej cechy w komunikacie debugowania?Jak zapewnić domyślną implementację debugowania?

mogę wymagać, aby osoby, które wdrażają moją cechę również wdrożyć Debug, ale nie lubię konieczności dodać, że dowolny wymóg:

trait MyTrait: Debug { ... } 

może po prostu wdrożyć Debug dla mojego cechy:

trait MyTrait { ... } 

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

To nie pozwala implementacji zastąpić Debug - to prawie tak, jakby funkcja nie jest wirtualna. Jak mogę to sprawić?

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

Wyjścia:

A { a: Data{ ... } } 

Czego chcę:

A { a: B(42) } 

chcę tylko pierwsze wyjście po B nie implementuje Debug.

+1

Brzmi jak [Zezwalaj na cechę, która implementuje jej cechę rodzicielską] (https://github.com/rust-lang/rfcs/issues/1024) jest tym, na co masz ochotę, z domyślną funkcją 'Debuguj' dla twojej cechy . – Lukazoid

Odpowiedz

5

Możesz stworzyć swoją własną metodę cech. Typy, które chcą mieć wzmocnione debugowanie i wdrożyć Debug może delegować:

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

Alternatywnym rozwiązaniem, które wymaga niestabilną funkcję specjalizacja

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

Zauważ, że to stawia obciążeń na pojemnik.