原始链接:生锈编程语言
作者:铁匠队
链接到第一个翻译:https://zhuanlan.zhihu.com/p/516660154
版权属于作者。对于业务重印,请与作者联系以获得授权。请指示非商业重印的来源。
有一些注释对中间的JavaScript开发人员有帮助。当学习Rust的守则,尤其是一些经验丰富的开发人员时,您通常会查看一些相对简单的库或项目需要学习。Rust的本地语法本身并不复杂。具有一定的打字稿经验的开发人员应该通过查看一些教程来熟悉基本语法,这与宏有关。尽管本文撰写了本文,但大多数开发人员不需要开发宏本身,但是每个人都使用宏观,理解他人代码的主要逻辑几乎是不明显的。尽管洪在Rust中是“先进的”,因为它的正交性与它的正交性有关雨果本身就认为应该更早地学习。此外,如果JavaScript开发人员以前曾接触过代码生成器和Babel,那么本章的内容将更加友善。
衍生宏,属性宏不像锈迹中的装饰器。从通用库提供的界面的视角,它也特别相似。因此,如果过去有经验的开发人员,他们也应该对此更加亲切章节。
整本书经常使用println!宏,但尚未引入宏观机理。Macro实际上是Rust的一系列功能的集合:声明人宏:使用marco_rules!代码和三个进程宏(Procedure Macros)::
我们来一一讨论这些内容,但首先,我们明白为什么需要这些特征,因为我们已经具有功能。
基本上,宏意味着某些代码可以生成其他代码。这项技术通常称为元编程(Hugo注意:代码生成器也属于这种类型的技术)。在附录C中,我们讨论了派生属性,以帮助您生成一系列的拖动。我们还使用println!和vec!宏观。当编译这些宏时,它们将作为代码开发,因此您无需编写这些代码。
金属编程可以帮助您减少笔迹和维护的量。当然,该功能也可以帮助您实现相似的功能。但是,宏的力量没有力量。
功能必须声明悲剧数量和类型。另一方面,您可以接收到任何数量的参数:我们可以调用println!(“ Hello”),或者您可以致电println!(“ hello {}”,名称)。此外,宏在编译阶段启动代码,因此宏可以在编译时实现类型的特征。不可能。在运行时,只有在编译期间才能发生道路实施。(雨果注意:JS可以在运行时生成道(这只是Rust的概念)。当然,如果您需要它。动态语言可以简单地实现非常强大的功能。某些情况。)
不好的地方是宏很复杂,因为您必须用锈蚀代码编写生锈代码(Hugo注意:任何Metada编程都不简单,包括JS。)。由于这种间接的性质,宏代码更加困难阅读,难以理解和难以维护。。,但作者的意图,因为本质是他创建了一组DSL)
与函数不同的另一个位置是,宏需要先定义或引入操作范围,并且可以在任何地方定义和使用该函数。
Rust中使用最广泛的宏是语句宏。有时被称为“宏”,“ Macro_rules!Macro”或“ Macro”或“ Macros”。声明宏与Rust的匹配语法更相似。语法是一种过程控制语法,接收表达式,然后匹配结果模式,然后执行与结果相匹配的代码。MACRO也将进行类似的比较:在这种情况下,将参数传递到RUST中是Rust的法律语法代码,然后通过宏的规则转换为代码,并由宏观编写的模板编写。
定义语句宏的语法是macro_rule!让我使用vec!要引入这种机制。第8章VEC!。例如,创建一个包含3个整数的新向量:
我们可以通过VEC创建任何类型的向量!韩国,例如2个整数或五个字符串切片。
我们不能使用函数来实现此功能,因为我们不知道参数输入的数量。熟悉TS,有一些与TS相似的。尽管Rust也有一个模型,但它与TS模型有很大不同。如果您报告错误,所有可能性都写了。填充状态机的幻觉。TS的代码也像这样。)
简化的VEC!宏:
注意:实际VEC!语句还包括提前分配的适当内存。这简化了此代码,以便更好地解释语句宏的概念。
macro_rules!稍后是宏的名称。只有一种模式与边缘(ARM)匹配:($($ x:expr),*),=>以后是生成此模式的代码。如果此模式成功匹配,由相应的代码组成的代码和输入参数将在最终代码中生成。由于这里只有一侧,只有此匹配条件。不符合此条件的输入将报告一个错误。复杂宏的宏。
这里匹配的规则与匹配不同,因为这里的语法与Rust的语法相匹配,而不是Rust的类型或价值。更全面的宏观语法,请参阅文档。
对于宏输入条件($($ x:expr),),在匹配语法内的$(),expr表示$()的后面。表明先前的模式将出现一次或多次。(雨果注意:它就像常规吗?宏语法实际上很简单,不要被高级愚弄。当然,宏仍然很困难,宏是宏的问题考虑到一个复杂的问题本身。)
当我们打电话:vec![1,2,3];时间,$ x模式匹配3表达式1、2和3。
现在让我们看一下与此方面相匹配的部分:
$()中的temp_vec.push($ x)是生成代码的一部分。*数字仍然表明零和多个。此匹配的特定数量取决于匹配条件的数量。
当我们打电话:vec![1,2,3];然后,生成了此代码。(Hugo Note:货物具有扩展的插头-in。对于语句宏,您可以了解有关扩展的更多信息。)
如果您宣讲任意参数,则最终生成符合上述条件的代码。
Macro_rules有一些奇怪的边界示例!将来,Rust将有第二个语句宏,类似于当前的机制,但它将解决这些边界问题。升级后,Macro_rules!语言,随时准备接受更改)记住这些。当然会讨论macro_rules!如果您对此作品特别感兴趣。请阅读“ Rust Macros的小书”。(Hugo Note:从进入的角度来看,您可以知道使用该机构。,功能上的功能和宏,并在Crates.io上使用宏可以满足您的需求。)
第二个宏是过程宏,它更像是一个函数(一种过程)。宏的过程在某些代码中,您可以操作这些代码,然后可以操作衬衫的某些代码。(Hugo:从结果来看陈述,宏之间没有区别。实际上,从JS的角度来看,它更像是Babel。您可以根据输入令牌进行更改)
尽管过程宏有三种类型:自定义衍生,类似属性和功能,但原理是相同的。
如果要创建一个宏观过程,则定义部分需要在您自己的课程中并定义特殊的类型。(Hugo注意:它等同于定义Babel插件,但是有一组Rust的系统。这些。宏将根据写作规则将宏转换为相应的代码。输入为代码,输入为代码。)将来可以消除此设计。
以下是过程宏的示例:
该过程宏接收一个令牌并输出一个tokenstream。tokenstream类型在proc_macro中定义,表明一系列令牌。这是该宏的核心机制。输入代码(将要生锈)转换为tokenstream,然后根据业务逻辑进行某些操作,最后生成tokenstream。此函数还可以重叠其他属性宏(#[some_attribute],看起来像装饰器的逻辑,装饰器的逻辑,也可以将其理解为一个链条电话),它可以在一个班级中定义多个过程。巴别首先。)
让我们看一下不同类型的流程宏。首先从自定义衍生宏开始,然后我们介绍了此宏和其他几种类型之间的区别。
我们创建一个名为hello_macro的板条箱名称,定义了一个hellomacro特征,关联的函数名称为hello_macro.通过使用此宏,用户的结构可以直接获得默认的hello_macro函数,而无需实现此特征。你好,宏!我的名字是Type!,Ampename是此衍生宏的结构的类型名称。
创建此宏的过程如下,首先
然后定义hellomacro道
这样,我们就有一个特征,并且这个triat函数。用户可以通过此牵引力直接实现相应的函数。
但是,用户每次都需要实现Hello_macro。如果Hello_macro的实现相似,则可以通过衍生宏实现。
由于Rust没有反射机制,因此我们在执行时不知道相应的名称类型。我们需要在编译过程中生成相应的代码。
接下来,定义宏过程。写这篇文章时,宏需要在其自身的火山口中。最后,该设计可能会改变。宏板条箱的协议是:对于一个名为foo的板条箱,自定义驱动器宏是foo_derive。我们在Hello_macro项目中创建Hello_macro_ederive板条。
我们的两个起重机密切相关,因此我们在Hello_macro板条箱中创建此类。如果我们想更改hello_macro的定义,我们还需要更改Hello_macro_derive的定义。这些两个板条箱是隔离的。应同时添加。为了简化依赖关系,我们可以让Hello_macro使用hello_macro_derive作为依赖项,然后导出此依赖项。但是,如果用户不想使用hello_macro_derive,则将添加此依赖项,则将被添加自动地。
这是Hello_macro_sacive作为一个过程宏的创建。需要添加syn和Quote。以下是该板条箱的货物。
将以下代码添加到lib.rs.note中,请注意,如果此代码不增加Impl_hello_macro的实现,则不会编译。
请注意,该代码在此处分为两个部分,这是Hello_macro_derive函数的一部分。此功能主要负责处理tokenstream,另一部分是在impl_hello_macro中。这是负责转换语法树的负责人:这种编写过程的方法可以更简单。大多数过程通常是相同的,前者的过程通常是相同的。从通常的角度来看,真正的差异是在impl_hello_macro中。这里的逻辑通常取决于流程宏业务。
我们介绍了三个板条箱:proc_macro,syn和quote.proc_macro是在生锈中构建的,无需在货物中引入。toml.proc_macro实际上是Rust Compiler的接口,该界面用于读取和操作Rust Code。
Syn Crate将Rust Code从字符串转换为操作结构。引号将SYN数据传输回Rust Code。这些起重机可以极大地简化流程宏的编写:编写完整的解析器来编写运行并不容易代码!
当[derive(hellomacro)]在类型上标记时,hello_macro_derive函数被调用。有这样的行为的原因是因为在定义hello_macro_derive时,它在函数前用#[proc_macro_derive(hellomacro)标记了它。
Hello_macro_derive将输入从Tokenstream转换为我们可以操作的数据结构。这就是为什么您需要将Syn.sync的Parse函数引入tokenstream中的原因。
上述结构的含义:正在处理标识符。这意味着煎饼的单位结构。其他字段代表其余的生锈代码。如果您想了解更多详细的内容,请参考它。
接下来,我们将开始定义Impl_hello_macro。此函数实现了添加到锈蚀代码的函数。在我们这样做之前,请注意派生宏的输出也是tokenstream。返回的tokenstream是添加代码后的代码板条箱,最终代码是处理完整的代码。
在此处调用SYN :: Parse时,您可能会发现您使用UNANRAP。如果报告了错误,则将中断。此处必须在此处完成,因为最终返回是tokenstream,而不是结果。这是简化代码以解释此问题。在生产代码中,您应该处理错误并提供更详细的信息错误信息,例如使用恐慌!或期望。
以下是代码:
使用上述代码,货物构建可以正常工作。如果要使用此代码,则需要添加两个依赖项。
现在执行以下代码,您可以看到Hello,宏!我叫煎饼!
接下来,让我们探索其他类型的过程宏。
属性宏和衍生宏是相似的,但是可以创建属性,除了衍生事故外,源只能对结构和枚举作用,而属性宏可以对其他事物(例如函数)作用。以下是属性宏的示例:例如,您在Web框架中制作了一个名为“路由到招标功能”的属性宏。
在这里,有两个参数,类型是tokenstream。第一个是属性的内容,get,“/”段,第二个是属性宏的语法部分。在此示例中,其余的FN索引(){}。
工作原理与衍生宏相同。
函数宏的使用更像是调用锈函数。函数宏有点像macro_rules!,它可以提供比函数更高的灵活性。例如,可以接受未知数的参数。但是Macro_rules!它只可以在上面章节的匹配语法中使用。函数宏接受tokenstream参数作为参数。像其他流程宏一样,您可以进行任何转换,然后返回到tokenstream。以下是函数宏SQL!
该宏接受SQL语句,以检查此SQL的语法是否正确。此功能比Macro_rules!此功能复杂得多!此SQL的宏!
此定义和自定义派生宏类类似于:在括号中接受令牌并返回生成的代码。
好吧,现在您有了一些新的生锈新工具,这些新工具可能不常用,但是您需要知道的是它们在需要的情况下操作的原理是什么。我们引入了一些复杂的主题。当您在错误的处理或其他代码中看到这些宏时,您可以识别这些概念和语法。本章的内容可以用作解决这些问题的索引。
原始:https://juejin.cn/post/7100107332654366756