rust宏(macro)詳解

前言

rust 學習曲線非常陡峭,但是基本語法也還算挺好理解,自動內存管理有點類似智能指針,基本看一下語法入門就可以大概理解,但是唯獨宏很難理解,語法非常晦澀。但是功能非常強大。聲明宏類似于c語言的宏處理,但是功能更強大。過程宏則類似于Android的注解編程,自定義AbstractProcessor,但是實現更優雅。
下面記錄一下宏處理的一些特點

正文

目前主流的文章都是翻譯自官方文檔,或者取部分Rust語言圣經,關鍵的部分特性就確實,只有rust宏詳解中非常詳細的介紹,這里簡要記錄一下特點

聲明宏

聲明宏主要是替代,主要是通過簡單的模式匹配,然后進行操作,這貌似非常容易處理面向對象的工廠模式,或者解決方法多參數操作,比如

macro_rules! vec {() => ($crate::__rust_force_expr!($crate::vec::Vec::new()));($elem:expr; $n:expr) => ($crate::__rust_force_expr!($crate::vec::from_elem($elem, $n)));($($x:expr),+ $(,)?) => ($crate::__rust_force_expr!(<[_]>::into_vec(// This rustc_box is not required, but it produces a dramatic improvement in compile// time when constructing arrays with many elements.#[rustc_box]$crate::boxed::Box::new([$($x),+]))));
}

這就是根據不同的匹配模式,=>的前半部分,替換成后半部。比如無參數的vec。因為這是系統接口,這里不在詳細介紹操作,只介紹匹配,

  1. ()是無參構造函數。
  2. ($elem:expr; $n:expr)這是匹配模式類似vec![1;5],這是創造一個size是5的,值是1的vec。
  3. 這個是匹配vec![1, 2, 3],這是構造一個內容是1、2、3的vec

第二個匹配模式中,$elem是為匹配的內容命名,方便后面使用,expr(一個表達式 (expression))指明匹配的元素,;就不用解釋,就是字面值。$n:expr同樣道理。
第二個匹配則稍微復雜一些,這里則用的是循環模式。循環是通過$(....)來指明的,括號內為循環內容,為了方便閱讀,則需要有分隔符和循環次數,這里是通過定義分隔符,+定義循環至少一次。$(,)?又是一個循環,循環內容則是。而循環一次則是最多一次。

所有的語句如下:

block:一個塊(比如一塊語句或者由大括號包圍的一個表達式)
expr:一個表達式 (expression)
ident:一個標識符 (identifier),包括關鍵字 (keywords)
item:一個條目(比如函數、結構體、模塊、impl 塊)
lifetime:一個生命周期注解(比如 'foo、'static)
literal:一個字面值(比如 “Hello World!”、3.14、‘🦀’)
meta:一個元信息(比如 #[…] 和 #![…] 屬性內部的東西)
pat:一個模式 (pattern)
path:一條路徑(比如 foo、::std::mem::replace、transmute::<_, int>)
stmt:一條語句 (statement)
tt:單棵標記樹
ty:一個類型
vis:一個可能為空的可視標識符(比如 pub、pub(in crate))

循環則如下:

反復捕獲的一般形式為 $ ( … ) sep rep。

$ 是字面上的美元符號標記
( … ) 是被反復匹配的模式,由小括號包圍。
sep 是可選的分隔標記。它不能是括號或者反復操作符 rep。常用例子有 , 和 ; 。
rep 是必須的重復操作符。當前可以是:

  1. ?:表示最多一次重復,所以此時不能前跟分隔標記。
  2. *:表示零次或多次重復。
  3. +:表示一次或多次重復。

過程宏

分為三類

  • 派生宏(Derive macro):用于結構體(struct)、枚舉(enum)、聯合(union)類型,可為其實現函數或特征(Trait)。
  • 屬性宏(Attribute macro):用在結構體、字段、函數等地方,為其指定屬性等功能。如標準庫中的#[inline]、#[derive(…)]等都是屬性宏。
  • 函數式宏(Function-like macro):用法與普通的規則宏類似,但功能更加強大,可實現任意語法樹層面的轉換功能。

聲明宏需要解析傳入的參數,進行匹配,而過程宏則需要自己解析傳入的內容,然后進行補充,生成代碼。這里需要解析TokenStream,舉個例子,就是用宏為一個結構體實現構建者模式。

#[derive(Builder)]
struct Command {// ...
}

最麻煩的是如何實現Builder

#[derive(Builder)]
struct Command {input_paht: String,// ...
}pub fn derive_builder(input: TokenStream) -> TokenStream {let input = parse_macro_input!(input as DeriveInput); // 解析input為 DeriveInput類型let input_ident = input.ident;  // 獲取原始類名let ident_builder = format_ident!("{}Builder", input_ident.to_string()); // 拼接builder類名if let Data::Struct(r) = input.data {   // 處理結構體let fields = r.fields;// 結構體屬性聲明let builder_fields = map_fields(&fields, &mut |(ident, ty)| {quote!(#ident: Option<#ty>,) });// 為builder增加set函數let builder_set_fields = map_fields(&fields, &mut |(ident, ty)| {quote!(pub fn #ident(mut self, value: #ty) -> Self {self.#ident = Some(value);self}) });// 獲取builder的屬性值let builder_lets = map_fields(&fields, &mut |(ident, _)| {quote!(let #ident = self.#ident.ok_or(format!("field {:?}  not set yet", stringify!(#ident),))?;)});// 初始化時的默認值let builder_fields_values = map_fields(&fields, &mut |(ident, _)| {quote!(#ident,)});quote!(impl #input_ident {pub fn builder() -> #ident_builder {#ident_builder::default()}}#[derive(Default)]pub struct #ident_builder {#builder_fields}impl #ident_builder {#builder_set_fieldspub fn build(self) -> Result<#input_ident, String> {#builder_letsOk(#input_ident{ #builder_fields_values })}}).into()} else {// 不支持非struct類型quote!().into()}
}fn map_fields<F>(fields: &Fields, mapper:&mut F) -> TokenStream2
whereF: FnMut((&Option<proc_macro2::Ident> ,  &Type)) -> TokenStream2,
{let fs = fields.iter().map(|field| mapper((&field.ident ,&field.ty)) );let stream2 = TokenStream2::from_iter(fs);stream2
}

這里為Command實現了builder方法如下:

impl Command{pub fn builder() -> CommandBuilder{CommandBuilder::default()}
}pub struct CommandBuilder{input_path: String,
}impl CommandBuilder{pub fn (mut self, value: String) -> Self {self.input_path = Some(value);self}pub fn build(self) -> Result<Command, String> {let input_path= self.input_path.ok_or(format!("field {:?}  not set yet", stringify!(input_path),))?;Ok(Command{ input_path })}}

屬性宏則可以傳入參數,讓控制更自由一些,這里就不在詳細介紹
函數式宏則相對比較簡單,類似聲明宏,但是可以不去匹配規則,更自由,功能更強大。

解析TokenStream需要依賴一些庫,這比較復雜,就不在詳細介紹。要結合自己代碼需求,慢慢理解。

分析工具
cargo.exe  install cargo-expandcargo.exe expand

后記

rust實在是復雜,這里解釋一些語法規則,以后遇到問題再補充。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/212843.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212843.shtml
英文地址,請注明出處:http://en.pswp.cn/news/212843.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

docker-ubuntu中基于keepalived+niginx模擬主從熱備完整過程

一、環境準備 &#x1f517;在Ubuntu中安裝docker 二、主機 1、環境搭建 1.1 鏡像拉取 docker pull ubuntu:16.041.2 創建網橋 docker network create -dbridge --subnet192.168.126.0/24 br11.3 啟動容器 docker run -it --name ubuntu-1 --privileged -v /home/vac/l…

為 Compose MultiPlatform 添加 C/C++ 支持(2):在 jvm 平臺使用 jni 實現桌面端與 C/C++ 互操作

前言 在上篇文章中我們已經介紹了實現 Compose MultiPlatform 對 C/C 互操作的基本思路。 并且先介紹了在 kotlin native 平臺使用 cinterop 實現與 C/C 的互操作。 今天這篇文章將補充在 jvm 平臺使用 jni。 在 Compose MultiPlatform 中&#xff0c;使用 jvm 平臺的是 An…

Kubernetes實戰(十)-升級k8s集群

1 Kubernetes(k8s) 集群升級過程 Kubernetes 使用 kubeadm 工具來管理集群組件的升級。在集群節點層面&#xff0c;升級 Kubernetes(k8s)集群的過程可以分為以下幾個步驟&#xff1a; 1&#xff09;檢查當前環境和配置是否滿足升級要求。 2&#xff09;升級master主節點&…

如何一個例子玩明白GIT

一個例子玩明白GIT GIT的介紹和教程五花八門&#xff0c;但實際需要用的就是建倉、推送、拉取等操作&#xff0c;這兒咱可以通過一個例子熟悉這些操作&#xff0c;一次性搞定GIT的使用方法學習。下面這個例子的內容是內容是建立初始版本庫&#xff0c;然后將數據復制到 "遠…

輕量封裝WebGPU渲染系統示例<45>- 材質組裝流水線(MaterialPipeline)燈光、陰影、霧(源碼)

當前示例源碼github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/material/src/voxgpu/sample/MaterialPipelineFog.ts 當前示例運行效果: 此示例基于此渲染系統實現&#xff0c;當前示例TypeScript源碼如下&#xff1a; export class MaterialPipelineFog {pr…

數組創建方法

數組的創建 1.let a[] 2.let anew Array(5) 3.let anew Array(1,2,3) 4.let a[1,2,3] 創建數組是空還是有值是以上四種寫法。但是如果沒給值的變量是undefined&#xff0c;再a[0]找不到這種變量的。所以當找某一個數需要已經是數組內存。不想給值可以給空數組。只要是數組…

MEMS制造的基本工藝介紹——晶圓鍵合

晶圓鍵合是一種晶圓級封裝技術&#xff0c;用于制造微機電系統 (MEMS)、納米機電系統 (NEMS)、微電子學和光電子學&#xff0c;確保機械穩定和氣密密封。用于 MEMS/NEMS 的晶圓直徑范圍為 100 毫米至 200 毫米&#xff08;4 英寸至 8 英寸&#xff09;&#xff0c;用于生產微電…

【重點】【環鏈表入口】142. 環形鏈表 II

題目 public class Solution {public ListNode detectCycle(ListNode head) {if (head null || head.next null) {return null;}ListNode slow head, fast head;while (fast ! null && fast.next ! null) {slow slow.next;fast fast.next.next;if (slow fast) …

SQL語句---更新數據

介紹 使用sql語句更新數據。 命令 update 表名 set 字段1值1[,字段2值2] [where 條件表達式];[]&#xff08;方括號&#xff09;內的表是表示可選。 例子 將a表id值等于1的數據的名稱改為666 update a set name666 where id1;

2023-12-05 Qt學習總結7

點擊 <C 語言編程核心突破> 快速C語言入門 Qt學習總結 前言二十 QTcpSocket QTcpServer網絡庫服務端代碼:客戶端代碼 二十一 QProcess進程類二十二 QThread線程總結 前言 要解決問題: 學習qt最核心知識, 多一個都不學. 二十 QTcpSocket QTcpServer網絡庫 QTcpSocket和…

持續集成交付CICD:Jenkins流水線實現Nexus制品晉級策略

目錄 一、理論 1.開發測試運維環境 二、實驗 1.Nexus制品晉級策略 一、理論 1.開發測試運維環境 &#xff08;1&#xff09;環境 1&#xff09;持續集成開發環境&#xff08;DEV: Development Environment&#xff09; 直接通過源代碼編譯打包&#xff0c;其會跑單元測試…

python 筆記 :trajectory_distance包(如何可以正確使用)【debug篇】

包的地址&#xff1a;maikol-solis/trajectory_distance (github.com) 1 模塊介紹 用Cython實現的Python模塊&#xff0c;用于計算二維軌跡之間的距離 trajectory_distance包提供了9種軌跡間的距離計算方法&#xff1a; SSPD&#xff08;對稱線段路徑距離&#xff09;OWD&a…

機器學習算法(9)——集成技術(Bagging——隨機森林分類器和回歸)

一、說明 在這篇文章&#xff0c;我將向您解釋集成技術和著名的集成技術之一&#xff0c;它屬于裝袋技術&#xff0c;稱為隨機森林分類器和回歸。 集成技術是機器學習技術&#xff0c;它結合多個基本模塊和模型來創建最佳預測模型。為了更好地理解這個定義&#xff0c;我們需要…

WLAN配置實驗

本文記錄了WLAN配置實踐的過程&#xff0c;該操作在華為HCIA中屬于相對較復雜的實驗&#xff0c;記錄過程備忘。這里不就WLAN原理解釋&#xff0c;僅進行配置實踐&#xff0c;可以作為學習原理時候的參考。本文使用華為ENSP進行仿真。實驗拓撲圖如下&#xff1a; 1.WLAN工作流程…

【electron】外語函數接口 FFI

? 目錄 ? &#x1f6eb; 導讀需求開發環境 1?? FFI概念優點注意事項 2?? 【廢棄】node-ffi3?? node-ffi-napi安裝&#xff08;windows系統下&#xff09;示例&#xff1a;MessageBoxA、NtSuspendProcess 4?? node-win32-api安裝示例&#xff1a;查找窗口并設置窗口標…

UE5數據傳遞-紋理貼圖

期待結果&#xff1a; 流程 1. 通過C寫入數據到紋理貼圖 2. 在材質中通過采樣能正確讀取寫入的數值 踩坑&#xff1a; 1. UE5之后&#xff0c;需要設置采樣類型&#xff0c;才能達到上圖效果&#xff0c;默認采樣類型做了插值計算 FColor中寫入 PF_B8G8R8A8 UTexture2D* Conve…

第四題:憧憬(JavaPythonC++實現)【第六屆傳智杯-新增場次-程序設計挑戰賽解題分析詳解復盤】

本文僅為【2023傳智杯-第二場】第六屆傳智杯程序設計挑戰賽-題目解題分析詳解的解題個人筆記,個人解題分析記錄。 本文包含:第六屆傳智杯程序設計挑戰賽題目、解題思路分析、解題代碼、解題代碼詳解(Java&Python&C++實現) 文章目錄 更新進度記錄第四題:憧憬(Java…

AI 繪畫 | Stable Diffusion 藝術二維碼制作

前言 這篇文章教會你如果用Stable Diffusion WEB UI制作藝術二維碼,什么是藝術二維碼呢?就是普通二維碼和藝術圖片融合后的二維碼圖片,如下圖所示。主要原理還是使用controlNet的control_v1p_sd15_qrcode_monster模型和光影模型control_v1p_sd15_brightness。 教程 準備…

【論文閱讀筆記】NeRF+Mip-NeRF+Instant-NGP

目錄 前言NeRF神經輻射場體渲染連續體渲染體渲染離散化 方法位置編碼分層采樣體渲染推導公式&#xff08;1&#xff09;到公式&#xff08;2&#xff09;部分代碼解讀相機變換&#xff08;重要&#xff01;&#xff09; Mip-NerfTo do Instant-NGPTo do 前言 NeRF是NeRF系列的…

DIP——邊緣提取與分割

1.使用canny算法進行邊緣提取 本實驗比較簡單&#xff0c;基本思路是對原圖像進行一個高斯模糊處理&#xff0c;用于去噪&#xff0c;之后轉換為灰度圖&#xff0c;直接調用cv庫中的canny記性邊緣提取。若想直接得到彩色邊緣&#xff0c;則通過按位與操作&#xff0c;將原始彩色…