首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对于具有未实现默认值的字段的结构,是否有一种实现默认值的短期方法?

对于具有未实现默认值的字段的结构,是否有一种实现默认值的短期方法?
EN

Stack Overflow用户
提问于 2021-07-12 10:56:41
回答 3查看 1.5K关注 0票数 12

我有一个包含20个字段的结构:

代码语言:javascript
复制
struct StructA {
    value1: i32,
    value2: i32,
    // ...
    value19: i32,
    day: chrono::NaiveDate,
}

我想把Default的特质强加给StructA。我试图将#[derive(Default)]添加到结构中,但chrono::NaiveDate没有实现Default

然后我尝试为Default实现StructA

代码语言:javascript
复制
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>
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-07-12 11:31:19

导数板条箱使这种事情变得轻而易举:

代码语言:javascript
复制
#[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
票数 16
EN

Stack Overflow用户

发布于 2021-07-12 11:28:11

这是一个相当肮脏的技巧,但您可以将您的约会包在一个Option中,并且它有一个Default的实现。这样您就不需要自己实现Default了,您可以派生它。为了保持StructA::default()的相同语义,您需要编写自己的方法(幸运的是,除了已经派生的Default::default()之外,Rust允许定义default()方法) 游乐场

代码语言:javascript
复制
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展开包装。

代码语言:javascript
复制
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);
}
票数 3
EN

Stack Overflow用户

发布于 2022-11-13 16:08:48

还有另一个板条箱叫做Educe https://docs.rs/educe/latest/educe/#default

一些代码片段。

代码语言:javascript
复制
#[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,
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68346169

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档