程序宏如何检查Option <Option <T >>的泛型类型并将其展平为单个Option?

问题描述

我正在编写一个派生过程宏,其中所有值都转换为Options。问题在于结构中的任何Option字段都可以包含在这Option类型中。就其本身而言,在我开始使用serde序列化数据之前,这并不是什么大问题。我希望能够跳过任何值为None的值,但是在某些情况下,它会变成类似Some(None)Some(CustomOption::None)之类的值。这两种情况都没有比简单的None更有意义,但是我不能只在派生字段上写#[serde(skip_serializing_if = "Option::is_none")]。当然,它们会以JSON格式输出null值。

基本上,我希望能够使用syn库检查派生字段的内部值的类型是否为Option并将其展平为单数Option<T>在派生结构中,而不是Option<Option<T>>类型。我希望Rust在泛型上具有基于类型的模式匹配,但这不是真的。

我可以想到两种解决方案,但是我真的想不起来如何实现它们。首先是遍历所有字段并找到Option,然后解开这些选项并重新包装,以使它们在外部仅具有一个Option。此解决方案的一个潜在问题是,在执行计算后,我可能不得不将它们重新包装在另一个Option中。第二种解决方案是找到Option并相应地修改生成代码,以便如果内部选项包含None,则整个内容变为None;基本上只有一个辅助函数,如果该字段是Option,则该函数输出布尔值。关于如何实施这些或更好的解决方案的任何想法?

这是一个代码示例:

#[derive(Macro)]
struct X {
    a: usize,b: SomeType<String>,c: Option<String>,}
struct GeneratedX {
    a: Option<usize>,b: Option<SomeType<String>>,c: Option<Option<String>>,}

使用这样的函数将所有值包装在“选项”中:

pub fn wrap_typ_in_options(&self) -> TokenStream {
    // self is a struct with the type Type in it along with some other items.
    let typ: syn::Type = self.typ();

    // attribute to check if should ignore a field.
    if self.should_ignore() {
        quote! { Option<#typ> }
    } else {
        quote! { Option<<#typ as module::Trait>::Type> }
    }
}

解决方法

我按照原始帖子中的第二个想法找到了解决此问题的方法。我使用了这样的函数来判断令牌是否为Option

let idents_of_path = path
    .segments
    .iter()
    .fold(String::new(),|mut acc,v| {
        acc.push_str(&v.ident.to_string());
        acc.push(':');
        acc
    });
vec!["Option:","std:option:Option:","core:option:Option:"]
    .into_iter()
    .find(|s| idents_of_path == *s)
    .and_then(|_| path.segments.last())

然后我添加了一个名为is_option的新方法,如果Type::Path是一个选项,该方法将返回一个布尔值。

pub fn is_option(&self) -> bool {
    let typ = self.typ();

    let opt = match typ {
        Type::Path(typepath) if typepath.qself.is_none() => Some(typepath.path.clone()),_ => None,};

    if let Some(o) = opt {
        check_for_option(&o).is_some()
    } else {
        false
    }
}

我根据此调用的结果修改了生成的代码,其方式与我处理各种属性的方式类似。对于我的特定用例,所有这些都应该很好用,因为不会将别名Option引入此生态系统。有点混乱,但现在可以完成工作。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...