3个不稳定版本

0.2.0 2024年5月27日
0.1.0 2024年4月25日

过程宏 中排名第1314

每月下载量32
用于 structz

MIT/Apache

12KB
163 代码行

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将字段名称转换为由一系列零大小类型组成的专用类型。我们称它们为“字段名称类型”。

最后,将字段名称类型和每个字段的类型数据打包,并将它们组合成Tuple(来自tuplez)。

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

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>)>()
);

依赖关系

~275–720KB
~17K SLoC