Skip to content

add BestEffortDebug<T>, an always-Debug wrapper over T irrespective of T: Debug / T: !Debug #49071

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
wants to merge 8 commits into from
3 changes: 3 additions & 0 deletions src/liballoc/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,9 @@ pub use core::fmt::{ArgumentV1, Arguments, write};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};

#[unstable(feature = "best_effort_debug", issue = "0")]
pub use core::fmt::BestEffortDebug;

use string;

/// The `format` function takes an [`Arguments`] struct and returns the resulting
Expand Down
1 change: 1 addition & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
#![feature(exact_chunks)]
#![feature(pointer_methods)]
#![feature(inclusive_range_fields)]
#![feature(best_effort_debug)]

#![cfg_attr(not(test), feature(fn_traits, placement_new_protocol, swap_with_slice, i128))]
#![cfg_attr(test, feature(test, box_heap))]
Expand Down
72 changes: 72 additions & 0 deletions src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1960,5 +1960,77 @@ impl<T: ?Sized + Debug> Debug for UnsafeCell<T> {
}
}

/// A wrapper around a type `T` where `T: Debug` possibly holds. If it does,
/// then the `Debug` impl of `T` will be used in
/// `impl Debug for BestEffortDebug<T>`.
/// Otherwise (if `T: !Debug`), `impl Debug for BestEffortDebug<T>`
/// will output with the type name of `T` and that `T` is `!Debug`.
///
/// `BestEffortDebug` is useful to avoid `Debug` bounds when trying to debug
/// things in generic code. Without `BestEffortDebug`, your generic call stack
/// will get infected with `T: Debug` bounds until the type is known.
///
/// This type is particularly useful for macro authors.
///
/// # Guarantees
///
/// `BestEffortDebug` makes the guarantee that
/// `impl<T> Debug for BestEffortDebug<T>` exists.
/// However, you should not rely on the stability of `BestEffortDebug`'s
/// output format. In particular, no guarantee is made that the `type_name`
/// is included when `T: !Debug` or that `<T as Debug>::fmt` is used when
/// `T: Debug`.
///
/// # Examples
///
/// `BestEffortDebug<T>` where `T: Debug` will use the `Debug`
/// implementation of `T`:
///
/// ```rust
/// #![feature(best_effort_debug)]
/// use std::fmt::BestEffortDebug;
///
/// assert_eq!(format!("{:?}", BestEffortDebug(0)), "0");
/// ```
///
/// `BestEffortDebug<T>` where `T: !Debug` is `Debug` and outputs the type name:
///
/// ```rust
/// #![feature(best_effort_debug)]
/// use std::fmt::BestEffortDebug;
///
/// struct NotDebug;
/// assert_eq!(format!("{:?}", BestEffortDebug(NotDebug)),
/// "[<unknown> of type main::NotDebug is !Debug]");
/// ```
#[unstable(feature = "best_effort_debug", issue = "0")]
#[derive(Copy, Clone)]
pub struct BestEffortDebug<T>(pub T);

#[unstable(feature = "best_effort_debug", issue = "0")]
impl<T> Debug for BestEffortDebug<T> {
fn fmt(&self, fmt: &mut Formatter) -> Result {
<Self as BEDInternal>::fmt(self, fmt)
}
}

trait BEDInternal {
fn fmt(&self, fmt: &mut Formatter) -> Result;
}

impl<T> BEDInternal for BestEffortDebug<T> {
default fn fmt(&self, fmt: &mut Formatter) -> Result {
use intrinsics::type_name;
write!(fmt, "[<unknown> of type {} is !Debug]",
unsafe { type_name::<T>() })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the is !Debug part of the message is jargon-y and hard to understand. My idea of an improvement would be "[<unknown value> of type {}]"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree on the hard to understand bit, how about this?

"[<unknown value> of type {}, which does not implement Debug]"

The point of this being that if a library user sees this, they will know that they should bug the library author with a request to add #[derive(Debug)].

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(That's much better, and I think the reason for that is good)

}
}

impl<T: Debug> BEDInternal for BestEffortDebug<T> {
fn fmt(&self, fmt: &mut Formatter) -> Result {
self.0.fmt(fmt)
}
}

// If you expected tests to be here, look instead at the run-pass/ifmt.rs test,
// it's a lot easier than creating all of the rt::Piece structures here.
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
#![feature(unboxed_closures)]
#![feature(untagged_unions)]
#![feature(unwind_attributes)]
#![feature(core_intrinsics)]

#![cfg_attr(stage0, allow(unused_attributes))]
#![cfg_attr(stage0, feature(never_type))]
Expand Down