#macro #unpacking #macro-derive #proc-macro #substructs #superstructs

fromsuper

用于从现有超结构中派生新子结构(可能解包 Options)的宏辅助工具

6 个版本

0.2.1 2023 年 12 月 9 日
0.2.0 2023 年 12 月 9 日
0.1.3 2023 年 3 月 30 日

#414 in Rust 模式

每月 27 次下载

MIT/Apache

17KB

fromsuper

fromsuper 提供了一个过程宏,帮助将(大型)超结构转换为(较小的)子结构,通过丢弃不需要的数据。它还可以在转换过程中自动解包 Option。如果需要解包 Option,则实现 TryFrom,否则实现 From

这在处理大型解析输出(其中只有子集真正需要)时非常有用。将此类结构减少到实际需要的数据可以提高可维护性。如果原始结构包含大量 Option,解包它们将验证所需数据的存在,并大大提高后续处理的便利性。

基本用法

通过将以下内容添加到您的 Cargo.toml 中,将 fromsuper 包含到您的项目中:

fromsuper = "0.2"

您可能还想 use derive 宏

use fromsuper::FromSuper;

derive 宏的选项通过使用 fromsuper 属性来指定。必要的选项是 from_type,定义要转换的超结构

struct Bar {
    a: u32,
    b: String,
    c: HashSet<u64>,
    d: ComplexData,
}

#[derive(FromSuper)]
#[fromsuper(from_type = "Bar")]
struct Foo {
    a: u32,
    c: HashSet<u64>,
}

let bar = Bar { ... };
let foo: Foo = bar.into(); // using Foo's derived implementation of From<Bar>

如果子结构的字段与原始字段名称不同,可以使用字段属性 rename_from 来指定映射

#[derive(FromSuper)]
#[fromsuper(from_type = "Bar")]
struct Foo {
    a: u32,
    #[fromsuper(rename_from = "c")]
    new_name: HashSet<u64>,
}

解包 Option

可以通过添加 unpack 参数来启用从原始结构体自动解包 Option。单个字段可以选择退出解包(unpack = false),例如,当不是所有原始字段都是 Option,或者当你可以容忍 None 值时。此外,对于实现 Default 的字段类型,可以通过指定 unpack_or_default = true 来用默认值替换 None 值。当启用解包时,将实现 TryFrom 而不是 From,以便在所需值是 None 时失败。

struct Bar {
    a: Option<u32>,
    b: String,
    c: Option<HashSet<u64>>,
    d: Option<ComplexData>,
    e: Option<u64>
}

#[derive(FromSuper)]
#[fromsuper(from_type = "Bar", unpack = true)]
struct Foo {
    #[fromsuper(unpack = false)]
    b: String,
    #[fromsuper(unpack_or_default = true)]
    c: HashSet<u64>,
    d: ComplexData,
    #[fromsuper(unpack = false)]
    e: Option<u64>
}

let bar = Bar { ... };
let foo: Foo = bar.try_into()?; // using Foo's derived implementation of TryFrom<Bar>

泛型

derive(FromSuper) 可以处理许多涉及泛型的情况。超结构和子结构体都可以有类型参数。然而,当指定超结构体时,如果它不在子结构体中,例如,无法确定 Bar<T> 中的 T 是类型参数还是具体类型。为了区分这两种含义,泛型类型参数必须以 # 符号作为前缀(如果它们没有被派生类型使用,建议始终使用 # 符号,即使它们也在派生类型中使用)。

struct Bar<T, U> {
    x: Vec<T>,
    y: Vec<U>,
    z: U,
}

#[derive(FromSuper)]
#[fromsuper(from_type = "Bar<#T,u32>")]
struct Foo<T> {
    x: Vec<T>,
    z: u32,
}

这样,如果子结构体的字段不需要类型参数,就可以减少其类型参数的数量。

超结构和子结构体的生命周期参数应该自动正确处理。

引用而不是消耗超结构体

如果超结构体可以或不应该被消耗,派生的子结构体可以被配置为只包含对原始值的引用而不是消耗它们。可以通过使用 make_refs 参数来激活这种行为。请注意,这只能针对整个结构体激活,不能针对每个字段单独激活。

struct Bar {
    a: Option<String>,
    b: String,
}

#[derive(FromSuper)]
#[fromsuper(from_type = "&'a Bar", unpack = true, make_refs = true)]
struct Foo<'a> {
    a: &'a String,
    #[fromsuper(unpack = false)]
    b: &'a String,
}

贡献

由于很难预测 proc 宏的所有可能的用法场景,可能存在没有被适当处理的情况。请通过提交问题来告知我。

许可证

本项目根据 MIT 许可证和 Apache 2.0 许可证的条款进行许可。您可以自由选择最适合您需求的许可证。

依赖关系

~2MB
~42K SLoC