attribute是rust编译器留给程序员的交互接口,一段代码可以编译产出为二进制机器码的过程通常来说,用户的代码是编译器的输入,当编译器认为代码有问题,而程序员认为没问题的时候,必须允许程序员和编译器有交互,允许程序员指导个别代码的处理方式。
下面列举几个比较有代表性的场景:
rustc要求结构体的命名需为骆驼体,否则就会给出警告,假如程序员‘故意’要给出一个结构体,但是用了python的下划线命名,为了让编译器忽略对命名格式的‘异议’,可以通过
#[allow(non_camlel_case_types)]
这个标记,把它放在结构体声明之前,编译器就可以按照程序员的意愿主动放过。而不是输出一堆警告。
所谓的临场应变经典场景主要是硬件和操作系统环境区别,比如当前这个程序员的操作系统是windows
, CPU架构是x86_64
又或者是arm64
, 代码编译的时候有些别人的代码确实不可能编译通过,程序员需要主动处理条件编译规则,指导编译器不同架构下同一个代码应该编译哪一个具体实现。对应的attribute类似#[cfg(windows)]
的标记说明具体信息。
rust了解之后你会知道有很多特有的术语,比如‘氧化’表示用rust去重写一些现有的库,或者用rust去实现一个特定的功能,但是毕竟很多东西编译之后会有一些历史命名习惯或者接口规则,不能按照rust本身的约定输出,我们可以通过
#![crate_type = "cdylib"]
#![create_name=''crypto3]
这样我们得到的名字是libcrypto3.so
,我们还特意多加了一个3
在o
后面,表示这个库是我们自己用rust氧化之后的版本; 否则我们得到的是一个原版命名的libcrate.rlib
这样的rust二进制库名字.
单测就是这样一个典型场景,相比于其他语言用文件名后缀,前缀,或者是引入某些包单独编码而言,rust支持的更为简单直接,只要有 #[test]
修饰的函数都会标记为测试函数,会在cargo test
命令下被拉出来单独执行。
这个用法就非常多样了,典型场景是用#[derive(Debug)]
类似的标记好,以便编译器自动给我们的类型添加特性.
#[derive(Debug)]
struct Point {
x:f32,
y:f32,
}
编译器现在会给Point类自动添加一个fmt
函数,这个函数功能类似java的toString
, 或者python的__repr__
等,注意:derive的意义为派生,但这里并没有一个默认的父类实现
, 代码都是编译器根据当前的类程成员变量自动添加并编译的。
derive
和trait
搭配使用共同左右,想要明白其中细节可以阅读本系列上关于trait
的单独篇章.
看了一些快速简单的例子,你应该知道attribute这个概念是什么内涵了,知道了内涵也就明白了概念,遇到的时候就不会有疑惑了。下一篇我们讲一下rust恼人的宏,用不用不重要,重要的时候通过了解rust,大家一起思考一下手头使用的语言到底哪里不好,为什么有新的设计。