首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在使用syn时,如何从Option<T>中获取T?

在使用syn时,如何从Option<T>中获取T?
EN

Stack Overflow用户
提问于 2019-03-21 07:56:45
回答 1查看 2.5K关注 0票数 7

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

怎样才能只获取"String"?我想在quote!中使用#ty来打印"String",而不是"Option<String>"

我想生成如下代码:

代码语言:javascript
复制
impl Foo {
    pub set_bar(&mut self, v: String) {
        self.bar = Some(v);
    }
}

开始于

代码语言:javascript
复制
struct Foo {
    bar: Option<String>
}

我的尝试是:

代码语言:javascript
复制
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"),
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-21 17:34:02

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

代码语言:javascript
复制
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>;。您不能处理这些任意的名称。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55271857

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档