Rust属性宏库into-attr-derive的使用:简化类型转换与属性派生

Rust属性宏库into-attr-derive的使用:简化类型转换与属性派生

安装

在项目目录中运行以下Cargo命令:

cargo add into-attr-derive

或者在Cargo.toml中添加以下行:

into-attr-derive = "0.2.1"

示例代码

下面是两个完整示例,展示如何使用into-attr-derive库:

基础示例

use into_attr_derive::IntoAttr;

// 定义简单结构体
#[derive(IntoAttr)]
struct Person {
    name: String,
    age: u32,
}

fn main() {
    // 创建实例
    let person = Person {
        name: "Alice".to_string(),
        age: 30,
    };

    // 转换为属性
    let attrs = person.into_attr();
    
    // 使用转换后的属性
    println!("Name: {}", attrs.name);
    println!("Age: {}", attrs.age);
}

高级示例

use into_attr_derive::IntoAttr;

// 嵌套结构体
#[derive(IntoAttr, Debug, Clone)]
struct Address {
    street: String,
    city: String,
    zip: String,
}

// 主结构体
#[derive(IntoAttr)]
struct Employee {
    id: u64,
    #[into_attr(skip)]  // 跳过该字段
    salary: f64,
    address: Address,   // 嵌套结构体
}

fn main() {
    // 创建地址实例
    let address = Address {
        street: "123 Main St".to_string(),
        city: "Metropolis".to_string(),
        zip: "12345".to_string(),
    };
    
    // 创建员工实例
    let employee = Employee {
        id: 42,
        salary: 75000.0,
        address: address.clone(),
    };
    
    // 转换为属性
    let attrs = employee.into_attr();
    
    // 输出结果
    println!("Employee ID: {}", attrs.id);
    println!("Address: {:?}", attrs.address);
}

工作原理

这个库通过属性宏自动为结构体实现类型转换,主要功能包括:

  1. 自动生成into_attr方法
  2. 保留字段原始类型
  3. 支持嵌套结构体转换
  4. 保持所有权语义

注意事项

  1. 使用非标准许可证
  2. 当前版本为0.2.1
  3. 维护者:Boris Zhguchev

1 回复

Rust属性宏库into-attr-derive的使用:简化类型转换与属性派生

into-attr-derive是一个Rust属性宏库,旨在简化类型转换和属性派生的过程。它通过提供自定义派生宏来减少样板代码,使类型转换更加直观和类型安全。

主要功能

  1. 自动派生IntoFrom trait实现
  2. 简化结构体之间的转换
  3. 支持属性配置来定制转换行为

安装

Cargo.toml中添加依赖:

[dependencies]
into-attr-derive = "0.1"

基本使用

简单类型转换

use into_attr_derive::IntoAttr;

#[derive(IntoAttr)]
struct User {
    name: String,
    age: u32,
}

#[derive(IntoAttr)]
struct UserDto {
    name: String,
    age: u32,
}

fn main() {
    let user = User {
        name: "Alice".to_string(),
        age: 30,
    };
    
    // 自动获得Into<UserDto>实现
    let user_dto: UserDto = user.into();
    
    println!("Name: {}, Age: {}", user_dto.name, user_dto.age);
}

自定义字段映射

use into_attr_derive::IntoAttr;

#[derive(IntoAttr)]
struct Person {
    first_name: String,
    last_name: String,
    #[into_attr(rename = "years")]
    age: u32,
}

#[derive(IntoAttr)]
#[into_attr(for = "Person")]
struct PersonView {
    full_name: String,
    years: u32,
}

impl From<Person] for PersonView {
    fn from(person: Person) -> Self {
        PersonView {
            full_name: format!("{} {}", person.first_name, person.last_name),
            years: person.age,
        }
    }
}

fn main() {
    let person = Person {
        first_name: "John".to_string(),
        last_name: "Doe".to_string(),
        age: 42,
    };
    
    let view: PersonView = person.into();
    println!("{} is {} years old", view.full_name, view.years);
}

高级用法

忽略字段

use into_attr_derive::IntoAttr;

#[derive(IntoAttr)]
struct Source {
    id: u64,
    name: String,
    #[into_attr(skip)]
    password: String,
}

#[derive(IntoAttr)]
struct Target {
    id: u64,
    name: String,
}

fn main() {
    let source = Source {
        id: 1,
        name: "admin".to_string(),
        password: "secret".to_string(),
    };
    
    let target: Target = source.into();
    // password字段被自动忽略
}

自定义转换逻辑

use into_attr_derive::IntoAttr;

#[derive(IntoAttr)]
struct Celsius(f64);

#[derive(IntoAttr)]
#[into_attr(from = "Celsius")]
struct Fahrenheit(f64);

impl From<Celsius> for Fahrenheit {
    fn from(c: Celsius) -> Self {
        Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
    }
}

fn main() {
    let boiling = Celsius(100.0);
    let boiling_f: Fahrenheit = boiling.into();
    println!("Water boils at {}°F", boiling_f.0);
}

属性配置选项

  1. #[into_attr(rename = "new_name")] - 重命名字段
  2. #[into_attr(skip)] - 跳过字段转换
  3. #[into_attr(default)] - 使用默认值
  4. #[into_attr(from = "SourceType")] - 指定源类型
  5. #[into_attr(for = "TargetType")] - 指定目标类型

注意事项

  1. 结构体字段名称和类型需要匹配才能自动转换
  2. 对于复杂转换,仍然需要手动实现From trait
  3. 宏展开后的代码可以使用cargo expand查看

into-attr-derive库通过减少样板代码,使类型转换更加简洁和安全,特别适合在DTO(Data Transfer Object)模式或API边界类型转换中使用。

完整示例Demo

下面是一个结合多种功能的完整示例:

use into_attr_derive::IntoAttr;

// 源数据结构
#[derive(IntoAttr)]
struct Employee {
    employee_id: u64,
    first_name: String,
    last_name: String,
    #[into_attr(rename = "age")]
    years_of_service: u8,
    #[into_attr(skip)]
    salary: f64,
    department: String,
}

// 目标数据结构
#[derive(IntoAttr)]
#[into_attr(for = "Employee")]
struct EmployeeProfile {
    id: u64,
    full_name: String,
    age: u8,
    dept: String,
    #[into_attr(default = "false")]
    is_manager: bool,
}

// 自定义转换逻辑
impl From<Employee> for EmployeeProfile {
    fn from(emp: Employee) -> Self {
        EmployeeProfile {
            id: emp.employee_id,
            full_name: format!("{} {}", emp.first_name, emp.last_name),
            age: emp.years_of_service,
            dept: emp.department,
            is_manager: false, // 默认值会被覆盖
        }
    }
}

fn main() {
    let employee = Employee {
        employee_id: 1001,
        first_name: "张".to_string(),
        last_name: "三".to_string(),
        years_of_service: 5,
        salary: 50000.0,
        department: "IT".to_string(),
    };
    
    // 自动转换
    let profile: EmployeeProfile = employee.into();
    
    println!("员工档案:");
    println!("ID: {}", profile.id);
    println!("姓名: {}", profile.full_name);
    println!("年龄: {}", profile.age);
    println!("部门: {}", profile.dept);
    println!("经理: {}", profile.is_manager);
}

这个完整示例展示了:

  1. 基本字段自动映射
  2. 字段重命名(years_of_service -> age)
  3. 跳过敏感字段(salary)
  4. 设置默认值(is_manager)
  5. 自定义转换逻辑(组合full_name)

输出结果将是:

员工档案:
ID: 1001
姓名: 张 三
年龄: 5
部门: IT
经理: false
回到顶部