#[derive(SafeDebug)]Expand description
Derives std::fmt::Debug with automatic redaction for sensitive fields.
§Supported Types
- Named structs
- Tuple structs
- Unit structs
- Enums (all variant types: unit, tuple, struct)
- Generic types (with lifetimes and type parameters)
- Nested structures
§Requirements
- Must also derive or implement
Facet - Sensitive fields marked with
#[facet(sensitive)] - For generic types, type parameters must implement
Debug
§Performance
There should be minimal runtime overhead:
- One
Self::SHAPEconst access perDebug::fmtcall - One
field.is_sensitive()check per field (O(1) bitflag check) - No heap allocations beyond standard
Debugformatting - Zero cost for non-sensitive fields (formatted normally)
Benchmarks show <1% overhead compared to hand-written Debug implementations, and of course a lot less tedium writing code.
§Security
Fails safe when metadata is unavailable:
- Structs: Redacts all fields with
"[REDACTED:NO_METADATA]" - Enums: Outputs only the type name, no variant or field data
§Varied examples
Basic struct with mixed sensitive/non-sensitive fields:
ⓘ
use facet::Facet;
use safe_debug::SafeDebug;
#[derive(Facet, SafeDebug)]
struct PatientRecord {
id: String, // public field
#[facet(sensitive)]
ssn: String, // redacted as [REDACTED]
#[facet(sensitive)]
medical_history: String, // redacted as [REDACTED]
}Enums with sensitive fields in specific variants:
ⓘ
#[derive(Facet, SafeDebug)]
enum ApiResponse {
Success { code: u16, data: String },
Error {
code: u16,
#[facet(sensitive)]
error_details: String, // only redacted in Error variant
},
Pending(u64),
}Generic types (automatically adds required trait bounds):
ⓘ
#[derive(Facet, SafeDebug)]
struct Container<T> {
id: u32,
#[facet(sensitive)]
secret: T, // T will be redacted
}
// Expands to: impl<T: Debug + Facet> Debug for Container<T>Types with lifetimes:
ⓘ
#[derive(Facet, SafeDebug)]
struct BorrowedData<'a> {
public: &'a str,
#[facet(sensitive)]
token: &'a str,
}