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

如何使用darling解析支持多形式的带嵌套参数的自定义派生属性?

解决Darling解析多形式自定义属性的问题

你的问题出在**MyAttr结构体默认的FromMeta实现不支持单纯的标识符(也就是att这种不带括号的形式)**。Darling对结构体类型的默认解析逻辑只接受带括号的键值对(比如att(...)),而当你写#[myderive(att)]时,它会把att识别为一个Meta::Path类型的元数据,和结构体期望的格式不匹配,所以抛出了UnexpectedFormat("word")错误。

要实现你想要的三种使用形式,我们需要为MyAttr自定义FromMeta实现,让它同时支持两种输入格式:单纯的标识符(无参数)和带括号的参数列表(带嵌套参数)。

正确的代码实现

首先修改你的类型定义,为MyAttr添加自定义的FromMeta逻辑:

use darling::{FromDeriveInput, FromMeta, Error};
use syn::{Meta, Path};

#[derive(FromDeriveInput)]
#[darling(attributes(myderive))]
struct MyDeriveInput {
    #[darling(default)]
    att: Option<MyAttr>,
}

#[derive(Debug, Clone, PartialEq, FromMeta, Default)]
struct MyAttr {
    #[darling(default)]
    value: Option<Path>,
}

// 自定义FromMeta实现,支持单纯的标识符和带括号的参数
impl FromMeta for MyAttr {
    fn from_meta(meta: &Meta) -> Result<Self, Error> {
        // 处理 `att` 这种无参数的情况:直接返回默认的MyAttr(value为None)
        if let Meta::Path(_) = meta {
            Ok(Self::default())
        } else {
            // 处理 `att(value = "String")` 这种带参数的情况:调用派生的FromMeta实现解析括号内的内容
            <Self as FromMeta>::from_meta(meta)
        }
    }
}

验证测试代码

你的测试现在可以正常通过了:

#[test]
fn test_att_without_args() {
    let derive_input = syn::parse_str(
        r#"
        #[derive(MyTrait)]
        #[myderive(att)]
        struct Foo(u64);
        "#,
    )
    .unwrap();
    let parsed: MyDeriveInput = FromDeriveInput::from_derive_input(&derive_input).unwrap();
    assert!(parsed.att.is_some());
    assert!(parsed.att.unwrap().value.is_none());
}

验证其他两种场景

你也可以添加测试来验证另外两种场景:

#[test]
fn test_no_att() {
    let derive_input = syn::parse_str(
        r#"
        #[derive(MyTrait)]
        struct Foo(u64);
        "#,
    )
    .unwrap();
    let parsed: MyDeriveInput = FromDeriveInput::from_derive_input(&derive_input).unwrap();
    assert!(parsed.att.is_none());
}

#[test]
fn test_att_with_value() {
    let derive_input = syn::parse_str(
        r#"
        #[derive(MyTrait)]
        #[myderive(att(value = "String"))]
        struct Foo(u64);
        "#,
    )
    .unwrap();
    let parsed: MyDeriveInput = FromDeriveInput::from_derive_input(&derive_input).unwrap();
    assert!(parsed.att.is_some());
    assert!(parsed.att.unwrap().value.is_some());
    assert_eq!(parsed.att.unwrap().value.unwrap().segments[0].ident.to_string(), "String");
}

原理说明

  • 当解析#[myderive(att)]时,meta是Meta::Path类型,我们直接返回MyAttr的默认值(value: None)。
  • 当解析#[myderive(att(value = "String"))]时,meta是Meta::List类型,我们调用派生的FromMeta实现来解析括号内的value参数。
  • 当不写att时,MyDeriveInput的att字段因为标记了#[darling(default)],会自动设为None。

这样就完美支持了你需要的三种使用形式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 19:47:51