You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Rust的deluxe库提取带#[cacheable(id)]标记的字段名?

实现方案

首先你需要先定义字段对应的属性结构体,用来匹配#[cacheable(id)]标记,再遍历结构体字段提取目标字段名,最后生成Trait实现代码。

1. 定义字段属性结构体

先补充字段层面的属性解析结构体,关联到cacheable属性组:

use deluxe::{ExtractAttributes, ParseAttributes};
use quote::quote;
use syn;

// 结构体层面的属性(你已定义的部分)
#[derive(Debug, ExtractAttributes, ParseAttributes)]
#[deluxe(attributes(cacheable))]
struct CacheableAttributes {
    directory: String,
}

// 字段层面的属性,匹配#[cacheable(id)]标记
#[derive(Debug, Default, ExtractAttributes, ParseAttributes)]
#[deluxe(attributes(cacheable))]
struct CacheableFieldAttributes {
    id: bool, // 标记当前字段是否为id字段
}

2. 提取并验证id字段

在派生宏实现中,遍历结构体字段,提取标记了id的字段,并做合法性校验:

fn cacheable_derive_impl(
    item: proc_macro2::TokenStream,
) -> deluxe::Result<proc_macro2::TokenStream> {
    // 解析输入的结构体定义
    let mut ast: syn::DeriveInput = syn::parse2(item)?;

    // 提取结构体上的directory属性
    let CacheableAttributes { directory } = deluxe::extract_attributes(&mut ast)?;

    // 提取并验证id字段
    let id_field_name = match &mut ast.data {
        syn::Data::Struct(s) => {
            let mut id_fields = Vec::new();
            for field in &mut s.fields {
                // 提取当前字段的cacheable属性
                let field_attrs = CacheableFieldAttributes::extract_attributes(field)?;
                if field_attrs.id {
                    // 仅处理有名字的结构体字段(排除元组结构体的无名字段)
                    if let Some(ident) = &field.ident {
                        id_fields.push(ident.clone());
                    } else {
                        return Err(deluxe::Error::custom("id字段不能是元组结构体的无名字段").span(field.span()));
                    }
                }
            }

            // 校验id字段数量:必须有且仅有一个
            match id_fields.len() {
                0 => return Err(deluxe::Error::custom("结构体必须包含一个标记了#[cacheable(id)]的字段").span(ast.span())),
                1 => id_fields.remove(0),
                _ => return Err(deluxe::Error::custom("结构体只能有一个标记了#[cacheable(id)]的字段").span(ast.span())),
            }
        }
        // 仅支持结构体类型
        syn::Data::Enum(_) | syn::Data::Union(_) => {
            return Err(deluxe::Error::custom("Cacheable派生宏仅支持结构体类型").span(ast.span()));
        }
    };

    // 生成Cacheable Trait的实现代码
    let struct_name = &ast.ident;
    let expanded = quote! {
        impl Cacheable for #struct_name {
            fn id(&self) -> String {
                self.#id_field_name.to_string()
            }

            fn cache_directory() -> &'static str {
                #directory
            }
        }
    };

    Ok(expanded)
}

3. 配套定义Cacheable Trait

确保你已经定义了对应的Trait供派生宏实现:

pub trait Cacheable {
    /// 返回唯一标识字符串
    fn id(&self) -> String;
    /// 返回缓存目录路径
    fn cache_directory() -> &'static str;
}

关键说明

  • 字段属性结构体CacheableFieldAttributes通过#[deluxe(attributes(cacheable))]与cacheable属性组关联,id: bool会自动匹配#[cacheable(id)]这种无值标记。
  • 遍历字段时做了合法性校验:必须存在且仅存在一个id字段,且不能是元组结构体的无名字段,避免非法使用场景。
  • 最终通过quote库生成Trait实现代码,直接使用提取到的字段名id_field_name访问目标字段。

内容的提问来源于stack exchange,提问作者Michael

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 13:38:18