最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

rust聲明式宏的實(shí)現(xiàn)

 更新時(shí)間:2023年12月07日 11:03:51   作者:zy010101  
聲明式宏使得你能夠?qū)懗鲱愃?match?表達(dá)式的東西,來(lái)操作你所提供的?Rust代碼,它使用你提供的代碼來(lái)生成用于替換宏調(diào)用的代碼,感興趣的可以了解一下

在 rust 中,我們一開(kāi)始就在使用宏,例如 println!, vec!, assert_eq! 等??雌饋?lái)宏和函數(shù)在使用時(shí)只是多了一個(gè) !。實(shí)際上這些宏都是聲明式宏(也叫示例宏或macro_rules!),rust 還支持過(guò)程宏,過(guò)程宏為我們提供了強(qiáng)大的元編程工具。

聲明式宏

聲明式宏類似于 match 匹配。它可以將表達(dá)式的結(jié)果與多個(gè)模式進(jìn)行匹配。一旦匹配成功,那么該模式相關(guān)聯(lián)的代碼將被展開(kāi)。和 match 不同的是,宏里的值是一段 rust 源代碼。所有這些都發(fā)生在編譯期,并沒(méi)有運(yùn)行期的性能損耗。下面是一個(gè)例子:

// 聲明一個(gè)add宏
macro_rules! add {
    ($a: expr, $b: expr) => {
        $a + $b
    };
}

fn main() {
    let a = 10;
    let b = 22;

    let _res = add!(a, b);
    let _res = add!(a+1, b);
    let _res = add!(a*2, b+3);
}

我們需要一個(gè)類似于 GCC -E 的方式來(lái)查看一下預(yù)處理階段之后的代碼。cargo-expand 正好提供了相應(yīng)的功能。使用 cargo 安裝 cargo-expand 即可。

cargo install cargo-expand

安裝 cargo-expand 之后,可以使用 cargo expand 命令來(lái)查看聲明式宏是如何被展開(kāi)的。上面的代碼在執(zhí)行cargo expand之后輸出如下所示:

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
    let a = 10;
    let b = 22;
    let _res = a + b;
    let _res = a + 1 + b;
    let _res = a * 2 + (b + 3);
}

可以看到,每一個(gè) _res 的右邊都被展開(kāi)了,并且如果傳入的參數(shù)是一個(gè)表達(dá)式,則會(huì)將整個(gè)表達(dá)式作為一個(gè)整體傳遞給宏。這就是某些地方提到的“Hygienic Macros”(有些地方也翻譯為衛(wèi)生宏,翻譯的很抽象)。最后一行代碼中傳入的b+3被當(dāng)做了一個(gè)整體。如果是在C/C++中,不會(huì)自動(dòng)將表達(dá)式作為整體,而是直接進(jìn)行字符串替換。而 Rust 編譯器會(huì)自動(dòng)處理變量名和作用域,確保宏展開(kāi)后的代碼不會(huì)引入未預(yù)料的變量沖突。下面是一個(gè)C/C++中使用宏的例子。

#include<stdio.h>
#define ADD(a, b) a + b;

int main() {
    int a = 10;
    int b = 22;
    int _res = ADD(a, b)
    _res = ADD(a+1, b)
    _res = ADD(a*2, b+3)
} 

同樣,我們使用 gcc -E main.c 來(lái)獲取預(yù)處理之后的代碼。由于展開(kāi)之后的代碼非常得多,我們只放上 main 函數(shù)中展開(kāi)的部分。

int main() {
    int a = 10;
    int b = 22;
    int _res = a + b;
    _res = a+1 + b;
    _res = a*2 + b+3;
}

可以看到,調(diào)用的代碼展開(kāi)之后,并沒(méi)有將 b+3 作為一個(gè)整體來(lái)處理,而是簡(jiǎn)單的進(jìn)行替換。因此,我們?cè)?C/C++ 中編寫宏要特別注意,宏參數(shù)在使用的時(shí)候必須加上括號(hào)?,F(xiàn)在我們來(lái)修復(fù)上面 C/C++ 代碼中的宏。

#include<stdio.h>
#define ADD(a, b) (a) + (b);

int main() {
    int a = 10;
    int b = 22;
    int _res = ADD(a, b)
    _res = ADD(a+1, b)
    _res = ADD(a*2, b+3)
} 

這樣,我們?cè)谑褂煤甑臅r(shí)候,就避免了意外結(jié)果的發(fā)生。這樣展開(kāi)之后的代碼如下所示:

int main() {
    int a = 10;
    int b = 22;
    int _res = (a) + (b);
    _res = (a+1) + (b);
    _res = (a*2) + (b+3);
}

我們接著來(lái)定義我們自己的 my_vec! 宏, 來(lái)對(duì)聲明式宏的相關(guān)語(yǔ)法做一個(gè)解釋。

macro_rules! my_vec {
    // 匹配 my_vec![]
    () => {
        std::vec::Vec::new()
    };
    // 匹配 my_vec![1,2,3]
    ($($el:expr), *) => {
        // 這段代碼需要用{}包裹起來(lái),因?yàn)楹晷枰归_(kāi),這樣能保證作用域正常,不影響外部。這也是rust的宏是 Hygienic Macros 的體現(xiàn)。 
        // 而 C/C++ 的宏不強(qiáng)制要求,但是如果遇到代碼片段,在 C/C++ 中也應(yīng)該使用{}包裹起來(lái)。
        {
            let mut v = std::vec::Vec::new();
            $(v.push($el);)*
            v
        }
    };
    // 匹配 my_vec![1; 3]
    ($el:expr; $n:expr) => {
        std::vec::from_elem($el, $n)
    };
}
  • 由于宏要在調(diào)用的地方展開(kāi),我們無(wú)法預(yù)測(cè)調(diào)用者的環(huán)境是否已經(jīng)做了相關(guān)的 use,所以我們使用的代碼最好帶著完整的命名空間。

  • 在聲明宏中,條件捕獲的參數(shù)使用 $ 開(kāi)頭的標(biāo)識(shí)符來(lái)聲明。每個(gè)參數(shù)都需要提供類型,這里 expr 代表表達(dá)式,所以 $el:expr 是說(shuō)把匹配到的表達(dá)式命名為 $el。$(...),* 告訴編譯器可以匹配任意多個(gè)以逗號(hào)分隔的表達(dá)式,然后捕獲到的每一個(gè)表達(dá)式可以用 $el 來(lái)訪問(wèn)。由于匹配的時(shí)候匹配到一個(gè) $(...)* (我們可以不管分隔符),在執(zhí)行的代碼塊中,我們也要相應(yīng)地使用 $(...)* 展開(kāi)。所以這句 $(v.push($el);)* 相當(dāng)于匹配出多少個(gè) $el 就展開(kāi)多少句 push 語(yǔ)句。
    反復(fù)捕獲反復(fù)捕獲的一般形式是$ ( ... ) sep rep,

     $ 是字面上的美元符號(hào)標(biāo)記
     ( ... ) 是被反復(fù)匹配的模式,由小括號(hào)包圍。
     sep 是可選的分隔標(biāo)記。它不能是括號(hào)或者反復(fù)操作符 rep。常用例子有 , 和 ; 。
     rep 是必須的重復(fù)操作符。當(dāng)前可以是:
     1. ?:表示最多一次重復(fù),所以此時(shí)不能前跟分隔標(biāo)記。
     2. *:表示零次或多次重復(fù)。
     3. +:表示一次或多次重復(fù)。
    
  • 如果傳入用冒號(hào)分隔的兩個(gè)表達(dá)式,那么會(huì)用 from_element 構(gòu)建 Vec。

我們來(lái)使用一下自定義的 my_vec! 宏

let mut v = my_vec!();
v.push(1);
println!("{:?}", v);
let v = my_vec![1, 2, 3, 4, 5];
println!("{:?}", v);
let v = my_vec!{1; 3};
println!("{:?}", v);

我們?cè)谑褂煤甑臅r(shí)候,可以使用(), [], {},都是可以的。但是一般都是按照約定成俗的方式來(lái)使用。例如:vec![1,2,3],而不是使用 vec!{1,2,3}。

這段宏調(diào)用,展開(kāi)以后,如下所示:

let mut v = std::vec::Vec::new();
v.push(1);
{
    ::std::io::_print(format_args!("{0:?}\n", v));
};
let v = {
    let mut v = std::vec::Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);
    v.push(5);
    v
};
{
    ::std::io::_print(format_args!("{0:?}\n", v));
};
let v = std::vec::from_elem(1, 3);
{
    ::std::io::_print(format_args!("{0:?}\n", v));
};

可以看到,let v = my_vec![1, 2, 3, 4, 5]; 被展開(kāi)為

let v = {
    let mut v = std::vec::Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);
    v.push(4);
    v.push(5);
    v
};

它帶上了我們?cè)诤甓x中的{},另外我們注意到println! 宏也被展開(kāi)了, 但是并沒(méi)有完全展開(kāi),其中還包含了一個(gè)format_args! 宏,我們來(lái)看一下,是否和println宏的定義一樣。

// println宏的定義
macro_rules! println {
    () => {
        $crate::print!("\n")
    };
    ($($arg:tt)*) => {{
        $crate::io::_print($crate::format_args_nl!($($arg)*));
    }};
}

可以看到,println帶有參數(shù)將會(huì)使用 format_args_nl! 宏,但是expand確是 format_args 宏。大概可能是因?yàn)槲臋n中說(shuō)format_args_nl宏是nightly模式下的吧!并沒(méi)有完全展開(kāi)是因?yàn)樵摵晔莾?nèi)置宏(rustc_builtin_macro)。

在使用聲明宏時(shí),我們需要為參數(shù)明確類型,剛才的例子都是使用的expr,其實(shí)還可以使用下面這些:

  • item,比如一個(gè)函數(shù)、結(jié)構(gòu)體、模塊等。
  • block,代碼塊。比如一系列由花括號(hào)包裹的表達(dá)式和語(yǔ)句。
  • stmt,語(yǔ)句。比如一個(gè)賦值語(yǔ)句。
  • pat,模式。
  • expr,表達(dá)式。剛才的例子使用過(guò)了。
  • ty,類型。比如 Vec。
  • ident,標(biāo)識(shí)符。比如一個(gè)變量名。
  • path,路徑。比如:foo、::std::mem::replace、transmute::<_, int>。 meta,元數(shù)據(jù)。一般是在 #[...]`` 和 #![…]`` 屬性內(nèi)部的數(shù)據(jù)。
  • tt,單個(gè)的 token 樹(shù)。
  • vis,可能為空的一個(gè) Visibility 修飾符。比如 pub、pub(crate)

聲明式宏還算比較簡(jiǎn)單。它可以幫助我們解決一些問(wèn)題。

  • 代碼重復(fù):聲明式宏可以幫助消除代碼中的冗余,通過(guò)將重復(fù)的代碼邏輯抽象成宏,從而減少代碼量并提高代碼的可讀性和維護(hù)性。
  • 代碼模板化:宏可以用于定義代碼模板,允許在編譯時(shí)根據(jù)不同的參數(shù)生成特定的代碼片段,從而實(shí)現(xiàn)代碼的泛化和重用。
  • 實(shí)現(xiàn)函數(shù)重載,宏可以匹配多種模式的參數(shù)來(lái)實(shí)現(xiàn)函數(shù)重載。

宏的缺點(diǎn)

宏目前的編寫無(wú)法得到IDE很好的支持,另外一點(diǎn)就是如無(wú)必要,就不要編寫宏。如果要編寫,那么盡量編寫聲明式宏,而不是過(guò)程宏。

  • 宏編寫復(fù)雜:過(guò)程宏的編寫可能相對(duì)復(fù)雜,特別是對(duì)于復(fù)雜的語(yǔ)法分析和代碼生成任務(wù),編寫和調(diào)試過(guò)程宏可能需要更多的時(shí)間和精力。
  • 可讀性下降:宏可能會(huì)導(dǎo)致代碼的可讀性下降,特別是在宏的展開(kāi)代碼復(fù)雜或嵌套層級(jí)較多時(shí),代碼可讀性可能變差。
  • 不利于錯(cuò)誤檢查:宏展開(kāi)發(fā)生在編譯期間,因此錯(cuò)誤信息可能不夠明確和直觀,難以定位宏展開(kāi)后的具體錯(cuò)誤位置。
  • 難以調(diào)試:宏展開(kāi)過(guò)程對(duì)于開(kāi)發(fā)者不是透明的,因此在調(diào)試過(guò)程中可能會(huì)遇到難以解決的問(wèn)題。

參考資料

到此這篇關(guān)于rust聲明式宏的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)rust聲明式宏內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • Rust生成隨機(jī)數(shù)的項(xiàng)目實(shí)踐

    Rust生成隨機(jī)數(shù)的項(xiàng)目實(shí)踐

    Rust標(biāo)準(zhǔn)庫(kù)中并沒(méi)有隨機(jī)數(shù)生成器,常見(jiàn)的解決方案是使用rand包,本文主要介紹了Rust生成隨機(jī)數(shù)的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Rust中print和println的區(qū)別實(shí)例解析

    Rust中print和println的區(qū)別實(shí)例解析

    Rust中print!和println!宏用于輸出,區(qū)別在于println!自動(dòng)換行,print!不換行,前者適用于同一行內(nèi)組合輸出,后者用于獨(dú)立日志或需換行場(chǎng)景,均支持格式化參數(shù),如{},本文給大家介紹Rust中print和println的區(qū)別,感興趣的朋友一起看看吧
    2025-06-06
  • 使用環(huán)境變量實(shí)現(xiàn)Rust程序中的不區(qū)分大小寫搜索方式

    使用環(huán)境變量實(shí)現(xiàn)Rust程序中的不區(qū)分大小寫搜索方式

    本文介紹了如何在Rust中實(shí)現(xiàn)不區(qū)分大小寫的搜索功能,并通過(guò)測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD)方法逐步實(shí)現(xiàn)該功能,通過(guò)修改運(yùn)行函數(shù)和獲取環(huán)境變量,程序可以根據(jù)環(huán)境變量控制搜索模式
    2025-02-02
  • Rust?Atomics?and?Locks內(nèi)存序Memory?Ordering詳解

    Rust?Atomics?and?Locks內(nèi)存序Memory?Ordering詳解

    這篇文章主要為大家介紹了Rust?Atomics?and?Locks內(nèi)存序Memory?Ordering詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Rust個(gè)人學(xué)習(xí)小結(jié)之Rust的循環(huán)

    Rust個(gè)人學(xué)習(xí)小結(jié)之Rust的循環(huán)

    這篇文章主要介紹了Rust個(gè)人學(xué)習(xí)小結(jié)之Rust的循環(huán),今天主要了解了Rust語(yǔ)言的3種循環(huán)方法:?loop、while、for,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • 使用Rust開(kāi)發(fā)小游戲完成過(guò)程

    使用Rust開(kāi)發(fā)小游戲完成過(guò)程

    這篇文章主要介紹了使用Rust開(kāi)發(fā)小游戲的完整過(guò)程,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-11-11
  • Rust字符串類型全解析(最新推薦)

    Rust字符串類型全解析(最新推薦)

    Rust語(yǔ)言中,字符串類型眾多,設(shè)計(jì)初衷是為了確保程序的安全、高效和靈活性,本文詳細(xì)解釋了Rust中不同的字符串類型,感興趣的朋友跟隨小編一起看看吧
    2024-09-09
  • Rust可迭代類型迭代器正確創(chuàng)建自定義可迭代類型的方法

    Rust可迭代類型迭代器正確創(chuàng)建自定義可迭代類型的方法

    在 Rust 中, 如果一個(gè)類型實(shí)現(xiàn)了 Iterator, 那么它會(huì)被同時(shí)實(shí)現(xiàn) IntoIterator, 具體邏輯是返回自身, 因?yàn)樽陨砭褪堑?這篇文章主要介紹了Rust可迭代類型迭代器正確創(chuàng)建自定義可迭代類型的方法,需要的朋友可以參考下
    2023-12-12
  • 使用?Rust?實(shí)現(xiàn)的基礎(chǔ)的List?和?Watch?機(jī)制示例流程

    使用?Rust?實(shí)現(xiàn)的基礎(chǔ)的List?和?Watch?機(jī)制示例流程

    本文給大家介紹使用Rust實(shí)現(xiàn)的基礎(chǔ)的List和Watch機(jī)制示例流程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2025-09-09
  • 如何在Rust中處理命令行參數(shù)和環(huán)境變量

    如何在Rust中處理命令行參數(shù)和環(huán)境變量

    在本章節(jié)中, 我們探討了Rust處理命令行參數(shù)的常見(jiàn)的兩種方式和處理環(huán)境變量的兩種常見(jiàn)方式,感興趣的朋友一起看看吧
    2023-12-12

最新評(píng)論

六盘水市| 基隆市| 根河市| 饶河县| 喀喇沁旗| 遵化市| 隆回县| 肇州县| 连州市| 阿坝| 广汉市| 互助| 商都县| 安远县| 尼木县| 民勤县| 黄浦区| 额尔古纳市| 耒阳市| 老河口市| 秀山| 凤庆县| 剑川县| 平舆县| 密山市| 会同县| 武平县| 五大连池市| 龙门县| 遂昌县| 榆林市| 宜昌市| 牟定县| 邮箱| 岳阳市| 类乌齐县| 清镇市| 和顺县| 南城县| 南投市| 南昌市|