使用“#[derive(Debug)]”覆盖结构的单个字段的默认实现
Override the default implementation for a single field of a struct using `#[derive(Debug)]`
我有一个包含 Web 应用程序一般配置数据的结构:
#[derive(Debug)]
pub struct AppConfig {
pub db_host: String,
pub timeout: Duration,
pub jwt_signing_key: String,
// more fields
}
jwt_signing_key
是一个对称的jwt secret,所以要保密/
这工作正常,但我希望能够记录 AppConfig
的内容以进行调试,而 JWT 机密不会出现在日志中。理想情况下,它会 return 类似于:
AppConfig {
db_host: "https://blah"
jwt_signing_key: "**********" // key obfuscated
}
一个选项是创建一个薄包装器:
pub struct AppConfig {
// ...
pub jwt_signing_key: JwtSigningKey,
}
pub struct JwtSigningKey(String);
// custom implementation of Debug
但这需要进行大规模重构并添加一个不必要的(在我看来)间接层。 (我不担心性能,但更多的是代码噪音)
AppConfig
相对较大,维护手动 Debug
实施是额外的工作,我不想每次添加字段时都必须这样做。
有没有更简单的解决方案?
查看 this thread. Essentially, the best way to do it right now is to use the derivative
crate,它允许你做这样的事情:
use derivative::Derivative;
use std::fmt;
fn obfuscated_formatter(val: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", "*".repeat(val.len()))
}
#[derive(Derivative)]
#[derivative(Debug)]
struct Foo {
normal: String,
#[derivative(Debug(format_with = "obfuscated_formatter"))]
secret: String
}
fn main() {
let foo = Foo {
normal: "asdf".into(),
secret: "foobar".into()
};
println!("{:?}", foo);
}
One option is to create a thin wrapper:
pub struct AppConfig {
// ...
pub jwt_signing_key: JwtSigningKey,
}
pub struct JwtSigningKey(String);
// custom implementation of Debug
我认为你应该选择这个选项。它的优点是 无论值在您的应用程序中的哪个位置结束, 它永远不会被意外打印 — 或以任何其他方式使用 — 而不是显式展开它,而替换 [= AppConfig
结构的 11=] 将仅协助处理该特定情况。它对重构时犯的错误也很稳健。
类型不必像JwtSigningKey
那样具体;它可能只是 pub struct Secret<T>(T);
并支持您最终使用的任何类型的秘密。甚至 libraries to provide such a type (see this thread for comments comparing two of them) 添加了更多功能,例如在掉落时将内存归零(这样内存内容的任何意外泄漏都不太可能泄露秘密)。
我有一个包含 Web 应用程序一般配置数据的结构:
#[derive(Debug)]
pub struct AppConfig {
pub db_host: String,
pub timeout: Duration,
pub jwt_signing_key: String,
// more fields
}
jwt_signing_key
是一个对称的jwt secret,所以要保密/
这工作正常,但我希望能够记录 AppConfig
的内容以进行调试,而 JWT 机密不会出现在日志中。理想情况下,它会 return 类似于:
AppConfig {
db_host: "https://blah"
jwt_signing_key: "**********" // key obfuscated
}
一个选项是创建一个薄包装器:
pub struct AppConfig {
// ...
pub jwt_signing_key: JwtSigningKey,
}
pub struct JwtSigningKey(String);
// custom implementation of Debug
但这需要进行大规模重构并添加一个不必要的(在我看来)间接层。 (我不担心性能,但更多的是代码噪音)
AppConfig
相对较大,维护手动 Debug
实施是额外的工作,我不想每次添加字段时都必须这样做。
有没有更简单的解决方案?
查看 this thread. Essentially, the best way to do it right now is to use the derivative
crate,它允许你做这样的事情:
use derivative::Derivative;
use std::fmt;
fn obfuscated_formatter(val: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", "*".repeat(val.len()))
}
#[derive(Derivative)]
#[derivative(Debug)]
struct Foo {
normal: String,
#[derivative(Debug(format_with = "obfuscated_formatter"))]
secret: String
}
fn main() {
let foo = Foo {
normal: "asdf".into(),
secret: "foobar".into()
};
println!("{:?}", foo);
}
One option is to create a thin wrapper:
pub struct AppConfig { // ... pub jwt_signing_key: JwtSigningKey, } pub struct JwtSigningKey(String); // custom implementation of Debug
我认为你应该选择这个选项。它的优点是 无论值在您的应用程序中的哪个位置结束, 它永远不会被意外打印 — 或以任何其他方式使用 — 而不是显式展开它,而替换 [= AppConfig
结构的 11=] 将仅协助处理该特定情况。它对重构时犯的错误也很稳健。
类型不必像JwtSigningKey
那样具体;它可能只是 pub struct Secret<T>(T);
并支持您最终使用的任何类型的秘密。甚至 libraries to provide such a type (see this thread for comments comparing two of them) 添加了更多功能,例如在掉落时将内存归零(这样内存内容的任何意外泄漏都不太可能泄露秘密)。