我有一个包含20个字段的结构:
struct StructA {
value1: i32,
value2: i32,
// ...
value19: i32,
day: chrono::NaiveDate,
}我想把Default的特质强加给StructA。我试图将#[derive(Default)]添加到结构中,但chrono::NaiveDate没有实现Default。
然后我尝试为Default实现StructA。
impl Default for StructA {
fn default() -> Self {
Self {
value1: Default::default(),
value2: Default::default(),
// ...
value19: Default::default(),
day: chrono::NaiveDate::from_ymd(2021, 1, 1),
}
}
}这段代码运行良好,但value1通过value19的部分是冗余的。是否有一个代码较少的解决方案?
StructA来通过serde-json反序列化JSON数据,所以我不能更改结构的定义。day: chrono::NaiveDate的值总是来自JSON数据,因此我希望避免使用day: Option<chrono::NaiveDate>。发布于 2021-07-12 11:31:19
导数板条箱使这种事情变得轻而易举:
#[derive(Derivative)]
#[derivative(Default)]
struct StructA {
value1: i32,
value2: i32,
// ...
value19: i32,
#[derivative(Default(value = "NaiveDate::from_ymd(2021, 1, 1)"))]
day: NaiveDate,
}如果您想避免外部板条箱,您的选项是:
Default::default(),一个简单的0也可以工作。day成为一个Option并派生Default,其缺点是它将默认为None,因此需要运行时成本,您必须使用unwrap()才能访问它。day成为一个新类型,它封装NaiveDate并实现Default以将其设置为所需的值,其缺点是您需要通过(零成本)字段或方法访问NaiveDate。发布于 2021-07-12 11:28:11
这是一个相当肮脏的技巧,但您可以将您的约会包在一个Option中,并且它有一个Default的实现。这样您就不需要自己实现Default了,您可以派生它。为了保持StructA::default()的相同语义,您需要编写自己的方法(幸运的是,除了已经派生的Default::default()之外,Rust允许定义default()方法) 游乐场
use chrono;
#[derive(Debug, Default)]
struct StructA {
value1: i32,
value2: i32,
value19: i32,
day: Option<chrono::NaiveDate>,
}
impl StructA {
fn default() -> Self {
let mut instance: Self = Default::default();
instance.day = Some(chrono::NaiveDate::from_ymd(2021, 1, 1));
instance
}
}
fn main() {
println!("{:?}", StructA::default());
// StructA { value1: 0, value2: 0, value19: 0, day: Some(2021-01-01) }
}这个版本的缺点是:
.unwrap()default,但一个是Self::default,它填充了我实现的日期,另一个是Default::default,它用None填充日期,您需要小心调用(调用StructA::default()调用Self::default())。编辑.请注意这个答案(详情见@user4815162342的评论)
简而言之,在带有.default()参数的泛型方法中,在一种类型中拥有两个不同的T: Default方法的最后一个缺点是危险的,因为在本例中将称为Default::default(),它将day字段初始化为None。最糟糕的是,编译器永远不会对您发出警告,从而迫使您在发生错误时花费时间进行调试。
@merErden提出了一种类似的方法,您可以再次将日期包装成另一种类型,您可以自己实现Default。这将确保您的字段将始终被初始化,但仍然强制您以某种方式“展开”该值。如果将NaiveDate包装成元组结构,您可以像instance.day.0一样简单地展开包装,也可以将Deref实现到这个包装器中,然后用*instance.day展开包装。
use chrono;
use std::ops::Deref;
#[derive(Debug)]
struct NaiveDateWrapper(chrono::NaiveDate);
impl Default for NaiveDateWrapper {
fn default() -> Self {
Self(chrono::NaiveDate::from_ymd(2021, 1, 1))
}
}
impl Deref for NaiveDateWrapper {
type Target = chrono::NaiveDate;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Default)]
struct StructA {
value1: i32,
value2: i32,
value19: i32,
day: NaiveDateWrapper,
}
fn main() {
let v = StructA::default();
println!("{:?}", v.day.0);
println!("{:?}", *v.day);
}发布于 2022-11-13 16:08:48
还有另一个板条箱叫做Educe https://docs.rs/educe/latest/educe/#default。
一些代码片段。
#[macro_use] extern crate educe;
#[derive(Educe)]
#[educe(Default)]
struct Struct {
#[educe(Default = 1)]
f1: u8,
#[educe(Default = 11111111111111111111111111111)]
f2: i128,
#[educe(Default = 1.1)]
f3: f64,
#[educe(Default = true)]
f4: bool,
#[educe(Default = "Hi")]
f5: &'static str,
#[educe(Default = "Hello")]
f6: String,
#[educe(Default = 'M')]
f7: char,
}
#[derive(Educe)]
#[educe(Default)]
enum Enum {
Unit,
#[educe(Default)]
Tuple(
#[educe(Default(expression = "0 + 1"))]
u8,
#[educe(Default(expression = "-11111111111111111111111111111 * -1"))]
i128,
#[educe(Default(expression = "1.0 + 0.1"))]
f64,
#[educe(Default(expression = "!false"))]
bool,
#[educe(Default(expression = "\"Hi\""))]
&'static str,
#[educe(Default(expression = "String::from(\"Hello\")"))]
String,
#[educe(Default(expression = "'M'"))]
char,
),
}
#[derive(Educe)]
#[educe(Default)]
union Union {
f1: u8,
f2: i128,
f3: f64,
f4: bool,
#[educe(Default = "Hi")]
f5: &'static str,
f6: char,
}https://stackoverflow.com/questions/68346169
复制相似问题