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

Attribute for skipping field of struct in Debug derives? #37009

Open
alexreg opened this issue Oct 6, 2016 · 38 comments
Open

Attribute for skipping field of struct in Debug derives? #37009

alexreg opened this issue Oct 6, 2016 · 38 comments
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@alexreg
Copy link
Contributor

alexreg commented Oct 6, 2016

Could we have something like this?

#[derive(Debug)]
struct Foo {
    pub a: A,
    pub b: B,
    #[skip_derive]
    pub c: C, // especially useful if C : !Debug
}

This could also feasibly be applied to other auto-derivations, of course (not just Debug).

@durka
Copy link
Contributor

durka commented Oct 7, 2016

At minimum we could certainly do it in a library with Macros 1.1 (then it would need a different name like #[derive(DebugEx)]).

@alexreg
Copy link
Contributor Author

alexreg commented Oct 7, 2016

Yeah, that would be a step forward at least. Would be nice to have it work for the built-in Debug though, I think.

@mcarton
Copy link
Member

mcarton commented Oct 7, 2016

I started such a library a few days ago that does that: https://github.com/mcarton/rust-derivative.
It's not finished, not really documented, not published on crates.io, but the POC is there 😅

@alexreg
Copy link
Contributor Author

alexreg commented Oct 7, 2016

Cool, thanks for sharing that. Is Macros 1.1 already fully implemented in nightly then? I may give it a go shortly.

On 7 Oct 2016, at 15:39, Martin Carton [email protected] wrote:

I started such a library a few days ago that does that: https://github.com/mcarton/rust-derivative https://github.com/mcarton/rust-derivative.
It's not finished, not really documented, not published on crates.io, but the POC is there 😅


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub #37009 (comment), or mute the thread https://github.com/notifications/unsubscribe-auth/AAEF3C1_mt5g26LCXeOHH8_2Xl8oPozmks5qxlmXgaJpZM4KQgfg.

@steveklabnik steveklabnik added C-enhancement Category: An issue proposing an enhancement or a PR with one. A-lang labels Oct 7, 2016
@steveklabnik steveklabnik added T-lang Relevant to the language team, which will review and decide on the PR/issue. and removed A-lang labels Mar 24, 2017
@alexreg
Copy link
Contributor Author

alexreg commented Feb 3, 2018

I'm wondering with all the recent Macro 2.0 developments whether the derive attribute macro might become a crate of its own in the future, such that it could be enhanced without changes to the actual compiler. A feature like this would then be much easier to get implemented, I suspect. Thoughts, @jseyfried @Manishearth?

@sfackler
Copy link
Member

sfackler commented Feb 3, 2018

Custom derives can already be implemented outside of the compiler.

@alexreg
Copy link
Contributor Author

alexreg commented Feb 3, 2018

@sfackler You misunderstand. I mean the workings of the derive attribute macro itself.

@sfackler
Copy link
Member

sfackler commented Feb 3, 2018

How would pulling libsyntax_ext::deriving into a different crate make this feature easier to implement? Difficulty of implementation is not the limiting factor here in any case.

@Manishearth
Copy link
Member

Manishearth commented Feb 3, 2018 via email

@sfackler
Copy link
Member

sfackler commented Feb 3, 2018

But that would mean that deriving can't be pulled out to a proc macro, not that pulling it out to a proc macro would make it easier to add extra control attributes, right?

@alexreg
Copy link
Contributor Author

alexreg commented Feb 3, 2018

I was thinking more of maintainability and neat, elegant separation of concerns. Not sure on the details of what @Manishearth just said, but that could be an interesting point.

@Manishearth
Copy link
Member

Manishearth commented Feb 3, 2018 via email

@alexreg
Copy link
Contributor Author

alexreg commented Feb 3, 2018

Is there no elegant way to get around this restriction?

@Manishearth
Copy link
Member

I'm not 100% clear on whether this restriction will be a problem here.

@eddyb knows

@irexiz
Copy link

irexiz commented May 21, 2020

Kind of digging up a buried corpse, but the crate https://crates.io/crates/derivative does that with
#[derivative(Debug="ignore")]

I think the issue can be closed with this. Thank you @mcarton!

@Allen-Webb
Copy link

A way of skipping fields in #[derive(Debug)] would be particularly useful for structs that have to use PhantomData.

@Xuanwo
Copy link
Contributor

Xuanwo commented Mar 11, 2022

The feature is also helpful for structs that have sensitive data like AWS s3 access_key_id & secret_access_key.

@MolotovCherry
Copy link

MolotovCherry commented Mar 4, 2023

For me, the problem often arises when using 3rd party libraries that don't implement Debug on their types, which disallows me from using derive(Debug) on my own types.

Sometimes all I need is just a quick way to derive Debug on my type rather than manually implementing it, so I'm ok with skipping a field. So I'm fairly certain that this would be incredibly helpful if it were in the std library

(While libraries exist which can do this, it would be nice to not have to rely on them)

@aljazerzen
Copy link

I think that the issue of deriving Debug for 3rd party types is a separate issue @MolotovCherry.


Regarding skipping fields, serde has a similar mechanism for serialization:

#[derive(Serialize)
struct MyStruct {
    // serialized always
    name: String,

    // serialized never
    #[serde(skip)]
    aws_secret: String,

    // serialized when `Option::is_none(&my_struct.extra)` is `false`
    #[serde(skip_serializing_if = "Option::is_none")] 
    extra: Option<String>
}

Debug could work similarly:

#[derive(Debug)
struct MyStruct {
    // debug always
    name: String,

    // debug never
    #[debug(skip)]
    aws_secret: String,

    // serialized when `Option::is_none(&my_struct.extra)` is `false`
    #[debug(skip_if = "Option::is_none")] 
    extra: Option<String>
}

The API is can be a bit simpler compared to serde, as serde has separate mechanisms for skipping serialization and de-serialization.

@MolotovCherry
Copy link

MolotovCherry commented Nov 26, 2023

@aljazerzen I think you misunderstand me.

Implementing a 3rd party trait for a 3rd party type is not allowed due to the orphan rule (though I suppose a workaround would be possible). I'm not arguing to overturn the orphan rule (or overturn it for only 1 specific instance).

I'm talking about skipping fields in my own types which derive debug, but can't derive debug, because a third party type doesn't. Which is related to what this issue is about (and could be solved by it)

#[derive(Debug)]
struct MyType {
    other_field: u64,

    // this third party type didn't implement Debug, now I can't derive debug
    // but I don't care about getting the details of ThirdPartyType, I just want to quickly debug MyType
    // having to do a manual Debug impl just to skip it would be cumbersome. I just want to program!
    //
    // If I do want to show data for ThirdPartyType, I will manually implement it,
    // but I don't need to nor want to right now
    //
    // the following would be nice
    #[debug(skip)]
    foo: ThirdPartyType
}

@aljazerzen
Copy link

Yes, I didn't understand what you meant. This is very related and a good argument for the feature.

@hagbard
Copy link

hagbard commented Dec 25, 2023

Alternatively, perhaps #[derive(Debug)] could have a flag or variant that just silently skips types without the Debug trait.
I'm hitting this a lot when trying to write code which interoperates between things like itertools and ndarray, since a lot of stuff in itertools demands Debug, and a bunch of stuff in ndarray doesn't provide it.

Can someone summarize the current (EOY '23) best workaround?

@tv42
Copy link
Contributor

tv42 commented Jan 1, 2024

Alternatively, perhaps #[derive(Debug)] could have a flag or variant that just silently skips types without the Debug trait.

I have several cases where my struct contains a binary byte slice with stuff that isn't interesting for debugging, or outright shouldn't be logged -- think crypto keys. Just because it implements Debug doesn't mean I want it in my debug output.

@Kobzol
Copy link
Contributor

Kobzol commented Jan 3, 2024

Alternatively, perhaps #[derive(Debug)] could have a flag or variant that just silently skips types without the Debug trait.

Note that this is currently not possible, because deriving happens before type checking.

@aljazerzen
Copy link

Can someone summarize the current (EOY '23) best workaround?

Use derivative - an alternative derive for implementing std::fmt::Debug.

@wiseaidev
Copy link

We need this feature so much because we don't want to expose private keys and credentials when debugging.

@Kobzol
Copy link
Contributor

Kobzol commented May 3, 2024

As an alternative, you can also achieve that by wrapping sensitive data into a Secret newtype, e.g. using secrecy.

@wiseaidev
Copy link

wiseaidev commented May 3, 2024

No! That's cheating! We need a built-in thing and not a third party crate, btw!

@Sytten
Copy link

Sytten commented May 4, 2024

Can someone summarize the current (EOY '23) best workaround?

Use derivative - an alternative derive for implementing std::fmt::Debug.

Derivative is effectively unmaintained, there are a couple alternatives but it really is needed built-in IMO

@geonnave
Copy link

We need this feature so much because we don't want to expose private keys and credentials when debugging.

Same use case here, it would be great to have such a skip attribute.

@palant
Copy link

palant commented Jul 4, 2024

This also goes as cheating but a wrapper type isn’t exactly much code:

use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};

struct NoDebug<T> {
    inner: T,
}

impl<T> Debug for NoDebug<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("skipped").finish()
    }
}

impl<T> From<T> for NoDebug<T> {
    fn from(value: T) -> Self {
        Self { inner: value }
    }
}

impl<T> Deref for NoDebug<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<T> DerefMut for NoDebug<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

impl<T> Default for NoDebug<T>
where
    T: Default,
{
    fn default() -> Self {
        Self {
            inner: T::default(),
        }
    }
}

Whatever fields shouldn’t appear in the debugging output can be wrapped then:

#[derive(Debug, Default)]
struct Debuggable {
    hidden: NoDebug<PhantomData<i32>>,
    visible: i32,
}

fn main() {
    dbg!(Debuggable::default());
}

This prints:

Debuggable::default() = Debuggable {
    hidden: skipped,
    visible: 0,
}

Playground

@PWhiddy
Copy link

PWhiddy commented Aug 29, 2024

+1 this would be a super useful feature :)

@barries
Copy link

barries commented Sep 2, 2024

+3.

This would ease:

+1: debugging (skipping large, useless, or non-Debug fields)
+1: security compliance (skipping credentials), especially in regulated industries
+1: HIPPA compliance (PII, PHI).

@Qix-
Copy link

Qix- commented Sep 2, 2024

@barries If the difference between HIPAA compliance and non-compliance is a #[derive(Debug)] then you severely need to revisit your data security practices and threat model. Information security should not be relevant to this particular conversation whatsoever; this is not a security feature.

@barries
Copy link

barries commented Sep 2, 2024 via email

@ant1fact
Copy link

ant1fact commented Dec 7, 2024

+1, this would be great for ergonomics.

@atezet
Copy link

atezet commented Dec 7, 2024

For people watching this ticket; the crate derive_more has (somewhat) recently added support for this in their 1.0 release.

@dolezvo1
Copy link

This is actually being actively worked on, somehow the issues don't seem to be linked in any way.

rust-lang/libs-team#334

#121050

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests