我用syn来解析 rust 迹代码.当我用field.ty读取一个命名字段的类型时,我得到了syn::Type.当我用quote!{#ty}.to_string()打印时,我得到了"Option<String>".

我怎么能只拿到"String"?我想用"Option<String>"代替#ty.

我想生成如下代码:

impl Foo {
    pub set_bar(&mut self, v: String) {
        self.bar = Some(v);
    }
}

struct Foo {
    bar: Option<String>
}

我的try :

let ast: DeriveInput = parse_macro_input!(input as DeriveInput);

let data: Data = ast.data;

match data {
    Data::Struct(ref data) => match data.fields {
        Fields::Named(ref fields) => {

            fields.named.iter().for_each(|field| {
                let name = &field.ident.clone().unwrap();

                let ty = &field.ty;
                quote!{
                    impl Foo {
                        pub set_bar(&mut self, v: #ty) {
                            self.bar = Some(v);
                        }
                    }
                };      
            });
        }
        _ => {}
    },
    _ => panic!("You can derive it only from struct"),
}

推荐答案

你应该像这个未经测试的例子那样做:

use syn::{GenericArgument, PathArguments, Type};

fn extract_type_from_option(ty: &Type) -> Type {
    fn path_is_option(path: &Path) -> bool {
        leading_colon.is_none()
            && path.segments.len() == 1
            && path.segments.iter().next().unwrap().ident == "Option"
    }

    match ty {
        Type::Path(typepath) if typepath.qself.is_none() && path_is_option(typepath.path) => {
            // Get the first segment of the path (there is only one, in fact: "Option"):
            let type_params = typepath.path.segments.iter().first().unwrap().arguments;
            // It should have only on angle-bracketed param ("<String>"):
            let generic_arg = match type_params {
                PathArguments::AngleBracketed(params) => params.args.iter().first().unwrap(),
                _ => panic!("TODO: error handling"),
            };
            // This argument must be a type:
            match generic_arg {
                GenericArgument::Type(ty) => ty,
                _ => panic!("TODO: error handling"),
            }
        }
        _ => panic!("TODO: error handling"),
    }
}

没有太多东西需要解释,它只是"展开"了一种类型的不同组成部分:

Type -> TypePath -> Path -> PathSegment -> PathArguments -> AngleBracketedGenericArguments -> GenericArgument -> Type.

如果有更简单的方法,我很乐意知道.


请注意,因为syn是一个解析器,所以它与令牌一起工作.你不能确定这是Option.例如,用户可以键入std::option::Option或写入type MaybeString = std::option::Option<String>;.你不能处理那些任意的名字.

Rust相关问答推荐

为什么导入Borrow将借入的呼叫改为Borrow::borrow而不是RefCell::borrow

把Vector3变成Vector4的绝妙方法

有没有办法模仿对象安全克隆?

如何在Rust中表示仅具有特定大小的数组

无法理解铁 rust &S错误处理

如何将像烫手山芋一样不透明的值从一个Enum构造函数移动到下一个构造函数?

使用铁 rust S还原对多个数组执行顺序kronecker积

将PathBuf转换为字符串

S在Cargo.toml中添加工作空间开发依赖关系的正确方法是什么?

在生存期内将非静态可变引用转换为范围内的静态可变引用

变量需要parse()中的显式类型

在Rust中克隆源自INTO_ITER()的迭代器的成本?

由于生存期原因,返回引用的闭包未编译

这是什么:`impl Trait for T {}`?

为相同特征的特征对象使用 move 方法实现特征

在多核嵌入式 Rust 中,我可以使用静态 mut 进行单向数据共享吗?

如何在 Rust 中按 char 对字符串向量进行排序?

bcrypt 有长度限制吗?

If let expression within .iter().any

在 Rust 中有条件地导入?