Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

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