Skip to main content
21 宏、注解、类型转换

21 宏、注解、类型转换

•1 min•136 words
宏,是编译器函数,是为了生成代码的函数

即所谓的元编程(metaprogramming)。这些宏以 展开 的方式来生成比你所手写出的更多的代码。

宏必须在调用前定义它或者引用,但函数则可以在任意地方定义与调用

因为编译器会进行多次扫描,将定义信息自动提到前边,而宏代码的运行在编译器执行的同时,所以编译器没法在执行宏之前提取宏到前边,宏代码在哪里,那么就什么时候执行宏。

宏可以接收可变数量的参数,因为宏将通过字符串分析来获得参数列表

这样性能虽然很差,但是宏本来就不是用来执行需要性能的事情,宏要做的是提供最强大的功能性即可。

声明宏 macro_rules!

声明宏能让我们匹配一些代码,然后执行预定分支

其核心概念是,声明宏允许我们编写一些类似 Rustmatch 表达式的代码。将一个值(传入代码字面)和包含相关代码的模式用于和前面提到的源代码字面值进行比较,一旦匹配成功,每个模式的相关代码会替换传递给宏的代码。

Summary

模式匹配母板 => 宏代码体;

#[macro_export]
macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}
注意要使用注解,表明这个宏是独立的,导入即可用

[!warning] 注意要使用#[macro_export]注解,表明这个宏是独立的,导入即可用
如果没有该注解,这个宏不能被引入作用域。(对于中间宏来说,只是某些宏的private宏,不能独立使用)

模式匹配母板

母板的作用就是将预定模式匹配,并拆解成宏变量

首先,一对括号包含了整个模式。我们使用美元符号($)在宏系统中声明一个变量来包含匹配该模式的 Rust 代码。美元符号明确表明这是一个宏变量而不是普通 Rust 变量。

语法简解

首先,一对括号包含了整个模式。() =>
使用美元符号($)在宏系统中声明一个变量来包含匹配该模式的 Rust 代码。
( $()) =>
$() 内则是 $x:expr ,其匹配 Rust 的任意表达式,并将该表达式命名为 $x。
( $($x:expr) )=>
在 $() 之后的逗号表示在每个与 $() 内代码匹配的实例之间必须出现一个字面量逗号分隔符。
($($x:expr),) =>
紧随逗号之后的 * 说明该模式匹配零个或更多个 * 之前的任何模式。
($($x:expr),) =>

宏代码块

宏代码块的整体布局

边界{}
某匹配分支模块 ()=>{};
分支模块的返回表达式 {代码块表达式}

macro_rules! name {
	()=>{
		{
			expr
		}
	};
}
宏代码块中的宏代码

$( temp_vec.push($x); )*表示模式匹配了多少次,这里就执行多少次,并且$x就是对应序号的模式匹配的值。

过程宏

过程宏的输入是注解的代码块,解析后形成的语法树

过程宏这个函数接收的参数和返回值都是TokenStream(令牌流)——也就是编译器眼中的“代码碎片”。

为了处理输入,你必须引入 syn 库把它解析成 Rust 的抽象语法树(AST),比如解析成 DeriveInput 结构体。然后你遍历这个结构体,用 quote 库把想生成的代码重新拼成 TokenStream 返回。

派生宏 derive

通过注解标注到结构体之后,派生宏根据注解的代码生成完善代码

派生宏的作用就是帮助我们一键完善、补充定义、实现。

属性宏

注解函数、结构体,帮助我们插入一些初始化、调用或者整体包裹调用

可以让我们专注于内部核心代码的书写。

类函数宏

传入别的语言,直接根据语言生成rust代码来实现效果

例如sql,像sql!("SELECT * FROM users")。它在编译期解析你传进去的字符串(比如 SQL),检查语法是否正确,甚至提前生成数据库访问代码。

条件编译

feature 特性

将crate分为不同的feature,引用者可按需引入

feature 是为了“按需启用”依赖库中的特定功能模块,从而精准控制编译时间、最终二进制文件大小,以及是否引入额外的系统依赖。

Summary
// 只有当 "webp" feature 被启用时,这个模块才会被编译

#[cfg(feature = “webp”)]
pub mod webp;

然后消费者就能通过feature指定引用库的那些部分。

有默认启用的feature

“default” 是一个特殊特性,默认会被启用default = ["ico"]

[dependencies]
my_image_processor = "0.1.0"
# 或者,显式启用特定特性
my_image_processor = { version = "0.1.0", features = ["webp"] }
# 或者,禁用默认特性,只启用自己需要的
my_image_processor = { version = "0.1.0", default-features = false, features = ["bmp"] }

类型转换

as 关键字
from trait 非常多的类型都实现了,完全转换
from_into
parse::<T>() parse trait也非常多的类型实现了
FromStr trait也非常多的类型实现了
TryFrom trait也非常的有用,使适用于无法完美转换的类型,由程序员自行决定逻辑。

切片和动态数组,转换成定长数组,需要使用定长数组的try_from

AsRef<T> trait 与 AsMut<T> trait 类型转换成T引用的函数实现 。