2017-02-19 9 views
8

Для большинства структур, которые вы создаете, чтобы помочь в отладке, считается хорошей практикой #[derive(Debug)]. Однако это невозможно, если ваша структура содержит тип без Debug, например, черты. Но если черта находится под моим контролем, есть ли что-то, что я могу сделать, чтобы позволить пользователям реализовать эту черту в сообщении об отладке?Как предоставить стандартную реализацию Debug?

Я мог бы потребовать, чтобы люди, которые реализуют свою черту и реализовать Debug, но я не люблю, чтобы добавить, что произвольное требование:

trait MyTrait: Debug { ... } 

Я мог бы просто реализовать Debug для моей черты:

trait MyTrait { ... } 

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

Это не позволяет реализовать переопределения Debug - это почти так, как если бы функция не была виртуальной. Как я могу сделать эту работу?

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

Выходы:

A { a: Data{ ... } } 

Что я хочу:

A { a: B(42) } 

Я хочу только первый выход, когда B не реализует Debug.

+1

Похоже, что [Разрешить черту для реализации своей родительской черты] (https://github.com/rust-lang/rfcs/issues/1024) - это то, что вам нужно, с использованием по умолчанию 'Debug' для вашей цели , – Lukazoid

ответ

5

Вы можете создать свой собственный метод признаков. Типы, которые хотели бы улучшили отладка и реализацией Debug могут делегировать:

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

Альтернативное решение, которое требует неустойчивой особенности специализации:

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

Обратите внимание, что это ставит нагрузку на контейнер.

Смежные вопросы