21 个版本 (5 个破坏性更改)

0.6.0 2024 年 5 月 27 日
0.5.6-alpha2024 年 5 月 27 日
0.4.0 2024 年 4 月 28 日
0.3.2 2024 年 4 月 26 日
0.1.2 2024 年 4 月 24 日

#688 in Rust 模式

每月 35 次下载

MIT/Apache

27KB
192

structz

Rust 中匿名结构体的实现。

概述

安装

cargo add structz

创建与访问

use structz::*;

let age = 26;
let mut person = stru! {
    name: "John Doe",
    age,    // captures the value of the variable with the same name
    tags: vec!["developer", "rustacean"],
};

// immutable borrow
assert_eq!(field!(&person.name), &"John Doe");

// mutable borrow
*field!(&mut person.age) += 1;
assert_eq!(field!(&person.age), &27);

// consume the struct and get the field value
let tags = field!(person.tags);
assert_eq!(tags, vec!["developer", "rustacean"]);

// `person` cannot be used anymore.

注意:具有相同字段但字段顺序不同的匿名结构体被视为同一类型的结构体。

use structz::*;

let rect1 = stru! { width: 1920, height: 1080 };
let rect2 = stru! { height: 1080, width: 1920 };
assert_eq!(rect1, rect2);

作为参数

use structz::*;

fn print_person(person: stru_t! {name: &str, age: u8, tags: Vec<&str> }) {
    println!(
        "{} is {} years old and has tags {:?}",
        field!(&person.name),
        field!(&person.age),
        field!(&person.tags)
    );
}

let person = stru! {
    tags: vec!["programmer", "artist"],
    name: "Alice",
    age: 30,
};
print_person(person);

使用 named_args 宏是一个更好的方法,它可以帮助您解包结构体

use structz::*;

#[named_args]
fn print_person(name: &str, age: u8, tags: Vec<&str>) {
    println!("{} is {} years old and has tags {:?}", name, age, tags);
}

let person = stru! {
    tags: vec!["programmer", "artist"],
    name: "Alice",
    age: 30,
};
print_person(person);

通过 subseq() 方法,您可以使用 tuplez 获取匿名结构体的子结构体

use structz::*;
use tuplez::TupleLike;

#[named_args]
fn print_person(name: &str, age: u8) {
    println!("{} is {} years old", name, age);
}

let alice = stru! {
    jobs: "programmer",
    name: "Alice",
    age: 30,
    children: vec!["Bob"],
};
print_person(alice.subseq());

let bob = stru! {
    name: "Bob",
    parent: vec!["Alice", "John"],
    age: 7,
    grade: 1,
};
print_person(bob.subseq());

let empty = stru! {
    name: "**Empty**",
    age: 0,
};
print_person(empty.subseq());   // Of course it is a sub-struct of itself

作为泛型类型

use stringz::ident;
use structz::*;

// `R1` and `R2` are "magic", used to indicate the position of the field in the structs,
// and these magic generic types will be automatically inferred by Rust.
// You should introduce a magic generic type for each field.
fn print_name_id<T, R1, R2>(any: &T)
where
    T: HasField<ident!(name), &'static str, R1>,
    T: HasField<ident!(id), usize, R2>,
{
    println!("{}", field!(&any.name));
    println!("{}", field!(&any.id));
}

let person = stru! {
    name: "John",
    age: 15,
    id: 1006,
    jobs: "Programmer",
};
let earth = stru! {
    name: "Earth",
    id: 3,
    galaxy: "Sol",
    satellites: vec!["Moon"],
};
print_name_id(&person);
print_name_id(&earth);

详细信息

structz 的实现基于 stringztuplez

首先,宏将输入字段按字典顺序排序,这确保了具有相同字段但字段顺序不同的匿名结构体具有相同的类型。

其次,通过 stringz 将字段名称转换为由零大小类型组成的专用类型。让我们称它们为“字段名称类型”。

最后,将字段名称类型和每个字段的 数据类型打包,并将它们组合成 tuplezTuple

由于字段名称实际上被替换为零大小类型,这不会给您带来任何开销

use structz::*;

assert_eq!(
    std::mem::size_of::<
        stru_t! {
            age: u8,
            name: &'static str,
            tags: Vec<&'static str>,
        },
    >(),
    std::mem::size_of::<(u8, &'static str, Vec<&'static str>)>()
);

依赖关系

~0.6–1MB
~20K SLoC