ToB企服应用市场:ToB评测及商务社交产业平台

标题: Rust宏之derive的设计及实战 [打印本页]

作者: 熊熊出没    时间: 2024-10-18 10:27
标题: Rust宏之derive的设计及实战
Rust宏可以极大的简化编写的难度,学习好宏可以更好的减少冗余代码。
宏的基本概念

Rust中的宏可以分为两大类:声明宏(Declarative Macros)和过程宏(Procedural Macros)。
宏的现实应用

  1. #[derive(Clone, Default)]
  2. struct HcluaMacro {
  3.     field: u32,
  4. }
复制代码
此时我们就可以使用:
  1. let obj = HcluaMacro::default();
  2. let obj_clone = obj.clone();
复制代码
雷同的还要在序列化的宏等。
过程宏的实战

目录为Rust中的lua库hclua做对象的绑定,可以快速的实现Rust对象在Lua中的快速使用绑定。
新建库

由于过程宏只能在单独的库中使用,所以此时我们必要新建单独的一个项目cargo new hclua-macro,并在新项目的Cargo.toml中添加
  1. [lib]
  2. proc-macro = true
复制代码
声明该项目为过程宏项目。
界说宏ObjectMacro

起首我们得界说ObjectMacro宏,那么我们必要声明:
  1. #[proc_macro_derive(ObjectMacro)]
  2. pub fn object_macro_derive(input: TokenStream) -> TokenStream {
  3.     TokenStream::new()
  4. }
复制代码
此处我们就可以在这基础上实现额外的代码,他将在声明该宏文件中自动添加代码。
我们做以下测试:
  1. #[proc_macro_derive(ObjectMacro)]
  2. pub fn object_macro_derive(input: TokenStream) -> TokenStream {
  3.     quote! {
  4.         fn this_is_macro_auto() {
  5.             println!("this_is_macro_auto auto func");
  6.         }
  7.     }.into()
  8. }
复制代码
其中quote!可以快速的生成代码块。
展开宏cargo-expand

接下我们必要宏在这个过期中帮我们生成了什么,我们借助以下工具cargo-expand,通过cargo install cargo-expand进行安装。
此时用cargo expand可以发现宏展开后的代码如下:
  1. #![feature(prelude_import)]
  2. #[prelude_import]
  3. use std::prelude::rust_2021::*;
  4. #[macro_use]
  5. extern crate std;
  6. use hclua_macro::ObjectMacro;
  7. struct HcluaMacro {
  8.     field: u32,
  9. }
  10. fn this_is_macro_auto() {
  11.     {
  12.         ::std::io::_print(format_args!("this_is_macro_auto auto func\n"));
  13.     };
  14. }
  15. fn main() {
  16.     this_is_macro_auto();
  17. }
复制代码
此时我们并没有处理跟类相关的任何东西,我们可以用parse_macro_input!将输入转成ItemStruct大概DeriveInput
  1. #[proc_macro_derive(ObjectMacro)]
  2. pub fn object_macro_derive(input: TokenStream) -> TokenStream {
  3.     let ItemStruct {
  4.         ident,
  5.         fields,
  6.         attrs,
  7.         ..
  8.     } = parse_macro_input!(input);
  9.     let name = ident.to_string();
  10.     quote! {
  11.         fn this_is_macro_auto() {
  12.             println!("struct name {}", #name);
  13.         }
  14.     }.into()
  15. }
复制代码
在quote中可以用#来序列化局数的变量数据。那么此时我们运行步伐,将会输出:
  1. struct name HcluaMacro
复制代码
类名正确的被打印出来。
字段处理

界说
  1. pub struct Field {
  2.     pub attrs: Vec<Attribute>,
  3.     pub vis: Visibility,
  4.     pub mutability: FieldMutability,
  5.     pub ident: Option<Ident>,
  6.     pub colon_token: Option<Token![:]>,
  7.     pub ty: Type,
  8. }
复制代码
属性处理

在此处我们界说了两种属性名称,hclua_field及 hclua_cfg,一种设置名称,一种设置是否可以在Lua中直接访问的字段名称,此时的宏界说:
  1. #[proc_macro_derive(ObjectMacro, attributes(hclua_field, hclua_cfg))]
  2. pub fn object_macro_derive(input: TokenStream) -> TokenStream {
  3.    
  4. }
复制代码
如果没有在此处界说的attrib,在类型里直接添加会报编译错误。
此处我们判断是否为hclua_field字段进行相应的加工。
  1. let functions: Vec<_> = fields
  2.     .iter()
  3.     .map(|field| {
  4.         let field_ident = field.ident.clone().unwrap();
  5.         if field.attrs.iter().any(|attr| attr.path().is_ident("hclua_field")) {
  6.             quote! {}
  7.         } else {
  8.             quote! {}
  9.         }
  10.     })
  11.     .collect();
复制代码
接下来将自动实现get及set方法。此处functions为TokenStream的数组,我们将用
  1. #(#functions)*
复制代码
将此部门内容做展开。
完整宏代码:
  1. use proc_macro::TokenStream;
  2. use quote::{format_ident, quote};
  3. use syn::{self, ItemStruct};
  4. use syn::parse_macro_input;
  5. #[proc_macro_derive(ObjectMacro, attributes(hclua_field, hclua_cfg))]
  6. pub fn object_macro_derive(input: TokenStream) -> TokenStream {
  7.     let ItemStruct {
  8.         ident,
  9.         fields,
  10.         attrs,
  11.         ..
  12.     } = parse_macro_input!(input);
  13.     let functions: Vec<_> = fields
  14.         .iter()
  15.         .map(|field| {
  16.             let field_ident = field.ident.clone().unwrap();
  17.             if field.attrs.iter().any(|attr| attr.path().is_ident("hclua_field")) {
  18.                 let get_name = format_ident!("get_{}", field_ident);
  19.                 let set_name = format_ident!("set_{}", field_ident);
  20.                 let ty = field.ty.clone();
  21.                 quote! {
  22.                     fn #get_name(&mut self) -> &#ty {
  23.                         &self.#field_ident
  24.                     }
  25.                     fn #set_name(&mut self, val: #ty) {
  26.                         self.#field_ident = val;
  27.                     }
  28.                 }
  29.             } else {
  30.                 quote! {}
  31.             }
  32.         })
  33.         .collect();
  34.     let name = ident.to_string();
  35.     quote! {
  36.         fn this_is_macro_auto() {
  37.             println!("struct name {}", #name);
  38.         }
  39.         impl #ident {
  40.             #(#functions)*
  41.         }
  42.     }.into()
  43. }
复制代码
将示例代码进行如下书写:
  1. use hclua_macro::ObjectMacro;
  2. #[derive(ObjectMacro)]
  3. struct HcluaMacro {
  4.     #[hclua_field]
  5.     field: u32,
  6.     not_field: u32,
  7. }
  8. fn main() {
  9.     this_is_macro_auto();
  10. }
复制代码
通过cargo expand将得到如下的代码:
  1. #![feature(prelude_import)]
  2. #[prelude_import]
  3. use std::prelude::rust_2021::*;
  4. #[macro_use]
  5. extern crate std;
  6. use hclua_macro::ObjectMacro;
  7. struct HcluaMacro {
  8.     #[hclua_field]
  9.     field: u32,
  10.     not_field: u32,
  11. }
  12. fn this_is_macro_auto() {
  13.     {
  14.         ::std::io::_print(format_args!("struct name {0}\n", "HcluaMacro"));
  15.     };
  16. }
  17. impl HcluaMacro {
  18.     fn get_field(&mut self) -> &u32 {
  19.         &self.field
  20.     }
  21.     fn set_field(&mut self, val: u32) {
  22.         self.field = val;
  23.     }
  24. }
  25. fn main() {
  26.     this_is_macro_auto();
  27. }
复制代码
自动实现了get及set方法,符合我们的要求。
注意事项


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4