Rust学习笔记5 属性与Cargo配置

github地址:https://github.com/bradyjoestar/rustnotes(欢迎star!)
pdf下载链接:https://github.com/bradyjoestar/rustnotes/blob/master/Rust%E8%AF%AD%E8%A8%80%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0.pdf
参考:
https://rustcc.gitbooks.io/rustprimer/content/ 《RustPrimer》
https://kaisery.github.io/trpl-zh-cn/ 《Rust程序设计语言-简体中文版》

5.1 属性
属性(Attribute)是一种通用的用于表达元数据的特性。
在rust中大量使用属性,对于go程序员而言,属性的大量使用很容易造成困惑。
属性有些类似与spring boot中的注解,但又有很大不同。首先java就是一门平台无关性的语言。
属性只能用于修饰rust中的item。
rust中的item包括:

  1. extern crate声明
  2. use声明
  3. 模块(模块是一个Item的容器)
  4. 函数
  5. type定义
  6. 结构体定义
  7. 枚举类型定义
  8. 常量定义
  9. 静态变量定义
  10. Trait定义
  11. 实现(Impl)
    这些Item是可以互相嵌套的,比如在一个函数中定义一个静态变量、在一个模块中使用use声明或定义一个结构体。

5.1 属性

5.1.1 属性的语法

Rust 包括两种 attribute :

![attribute属性描述]

第一种的含义是:

这种#后面有一个!的形式,表示这个属性值应用在当前的对象。通常,这样的attribute是针对当前文件设置的。当前文件可能包括两种对象,一个是crate,另一个可能是module。需要根据文件的上下文来判断。

判断的依据是,如果是src/mod.rs 或者 src/lib.rs 那么这个设置就是针对 crate 的,因为这两个文件是crate的root module。

[attribute属性描述]

第二种含义是:

这种前面只有#号的属性,表示属性的设置应用到属性下面的紧挨着的元素,即item上。

Rust attribute 正则表达式语法:

attribute : '#' '!' ? '[' meta_item ']' ;
meta_item : ident [ '=' literal  | '(' meta_seq ')' ] ? ;
meta_seq : meta_item [ ',' meta_seq ] ? ;

meta可以进行无限嵌套。
参看一些rust attribute的例子去理解它的语法:

#[cfg(not(target_feature = "crt-static"))]
#[derive(PartialEq, Clone)]
#![crate_name = "mycrate"]
#[target_feature(enable = "avx2")]
#[link(name = "CoreFoundation", kind = "framework")]
#![allow(clippy::filter_map)]
#[cfg_attr(linux, path = "linux.rs")]
#[cfg_attr(windows, path = "windows.rs")]

按照语法可以写出一个这样例子:

#[target(windows,not(config=”true”,ubuntu(“config”=false,sixteen)))]

翻译过来的语法规则描述如下:
1.单个标识符代表的属性名,如#[unix]
2.单个标识符代表属性名,后面紧跟着一个=,然后再跟着一个Value,组成一个键值对,如#[link(name = "openssl")]
3.单个标识符代表属性名,后面跟着一个逗号隔开的子属性的列表,如#[cfg(and(unix, not(windows)))]

5.1.2 几种常见的属性

官方的attribute文档:

https://doc.rust-lang.org/reference/attributes.html

RustPrimer文档:

https://rustcc.gitbooks.io/rustprimer/content/attr-and-compiler-arg/attribute.html

几种常用的attribute如下:

5.1.2.1.应用于crate的属性

• crate_name - 指定Crate的名字。如#[crate_name = "my_crate"]则可以让编译出的库名字为libmy_crate.rlib。
• crate_type - 指定Crate的类型,有以下几种选择
o "bin" - 编译为可执行文件;
o "lib" - 编译为库;
o "dylib" - 编译为动态链接库;
o "staticlib" - 编译为静态链接库;
o "rlib" - 编译为Rust特有的库文件,它是一种特殊的静态链接库格式,它里面会含有一些元数据供编译器使用,最终会静态链接到目标文件之中。
• feature - 可以开启一些不稳定特性,只可在nightly版的编译器中使用。
• no_builtins - 去掉内建函数。
• no_main- 不生成main这个符号,当你需要链接的库中已经定义了main函数时会用到。
• no_start - 不链接自带的native库。
• no_std - 不链接自带的std库。
• plugin - 加载编译器插件,一般用于加载自定义的编译器插件库。

5.1.2.2 应用于函数的属性

• main - 把这个函数作为入口函数,替代fn main,会被入口函数(Entry Point)调用。
• plugin_registrar - 编写编译器插件时用,用于定义编译器插件的入口函数。
• start - 把这个函数作为入口函数(Entry Point),改写 start language item。
• test - 指明这个函数为单元测试函数,在非测试环境下不会被编译。
• should_panic - 指明这个单元测试函数必然会panic。
• cold - 指明这个函数很可能是不会被执行的,因此优化的时候特别对待它。

5.1.2.3 应用于FFI的属性

extern块可以应用以下属性
• link_args - 指定链接时给链接器的参数,平台和实现相关。
• link - 说明这个块需要链接一个native库,它有以下参数:
o name - 库的名字,比如libname.a的名字是name;
o kind - 库的类型,它包括
 dylib - 动态链接库
 static - 静态库
 framework - OS X里的Framework
5.1.2.4 条件编译
有时候,我们想针对不同的编译目标来生成不同的代码,比如在编写跨平台模块时,针对Linux和Windows分别使用不同的代码逻辑。
条件编译基本上就是使用cfg这个属性,直接看例子

#[cfg(target_os = "macos")]
fn cross_platform() {
    // Will only be compiled on Mac OS, including Mac OS X
}

#[cfg(target_os = "windows")]
fn cross_platform() {
    // Will only be compiled on Windows
}

// 若条件`foo`或`bar`任意一个成立,则编译以下的Item
#[cfg(any(foo, bar))]
fn need_foo_or_bar() {

}

// 针对32位的Unix系统
#[cfg(all(unix, target_pointer_width = "32"))]
fn on_32bit_unix() {

}

// 若`foo`不成立时编译
#[cfg(not(foo))]
fn needs_not_foo() {

}

其中,cfg可接受的条件有

• debug_assertions - 若没有开启编译优化时就会成立。
• target_arch = "..." - 目标平台的CPU架构,包括但不限于x86, x86_64, mips, powerpc, arm或aarch64。
• target_endian = "..." - 目标平台的大小端,包括big和little。
• target_env = "..." - 表示使用的运行库,比如musl表示使用的是MUSL的libc实现, msvc表示使用微软的MSVC,gnu表示使用GNU的实现。 但在部分平台这个数据是空的。
• target_family = "..." - 表示目标操作系统的类别,比如windows和unix。这个属性可以直接作为条件使用,如#[unix],#[cfg(unix)]。
• target_os = "..." - 目标操作系统,包括但不限于windows, macos, ios, linux, android, freebsd, dragonfly, bitrig, openbsd, netbsd。
• target_pointer_width = "..." - 目标平台的指针宽度,一般就是32或64。
• target_vendor = "..." - 生产商,例如apple, pc或大多数Linux系统的unknown。
• test - 当启动了单元测试时(即编译时加了--test参数,或使用cargo test)。
还可以根据一个条件去设置另一个条件,使用cfg_attr,如

[cfg_attr(a, b)]

这表示若a成立,则这个就相当于#[cfg(b)]。
5.1.2.5 Linter参数
目前的Rust编译器已自带的Linter,它可以在编译时静态帮你检测不用的代码、死循环、编码风格等等。Rust提供了一系列的属性用于控制Linter的行为
• allow(C) - 编译器将不会警告对于C条件的检查错误。
• deny(C) - 编译器遇到违反C条件的错误将直接当作编译错误。
• forbit(C) - 行为与deny(C)一样,但这个将不允许别人使用allow(C)去修改。
• warn(C) - 编译器将对于C条件的检查错误输出警告。
编译器支持的Lint检查可以通过执行rustc -W help来查看。
5.1.2.6 编译特性
在非稳定版的Rust编译器中,可以使用一些不稳定的功能,比如一些还在讨论中的新功能、正在实现中的功能等。Rust编译器提供一个应用于Crate的属性feature来启用这些不稳定的功能,如

![feature(advanced_slice_patterns, box_syntax, asm)]

具体可使用的编译器特性会因编译器版本的发布而不同,具体请阅读官方文档。
属性较为繁琐,以官方文档为主。

5.2 cargo参数配置

5.2.1 package配置

[package]
 # 软件包名称,如果需要在别的地方引用此软件包,请用此名称。
name = "hello_world"

# 当前版本号,这里遵循semver标准,也就是语义化版本控制标准。
version = "0.1.0"    # the current version, obeying semver

# 软件所有作者列表
authors = ["you@example.com"]

# 非常有用的一个字段,如果要自定义自己的构建工作流,
# 尤其是要调用外部工具来构建其他本地语言(C、C++、D等)开发的软件包时。
# 这时,自定义的构建流程可以使用rust语言,写在"build.rs"文件中。
build = "build.rs"

# 显式声明软件包文件夹内哪些文件被排除在项目的构建流程之外,
# 哪些文件包含在项目的构建流程中
exclude = ["build/**/*.o", "doc/**/*.html"]
include = ["src/**/*", "Cargo.toml"]

# 当软件包在向公共仓库发布时出现错误时,使能此字段可以阻止此错误。
publish = false

# 关于软件包的一个简短介绍。
description = "..."

# 下面这些字段标明了软件包仓库的更多信息
documentation = "..."
homepage = "..."
repository = "..."

# 顾名思义,此字段指向的文件就是传说中的ReadMe,
# 并且,此文件的内容最终会保存在注册表数据库中。
readme = "..."

# 用于分类和检索的关键词。
keywords = ["...", "..."]

# 软件包的许可证,必须是cargo仓库已列出的已知的标准许可证。
license = "..."

# 软件包的非标许可证书对应的文件路径。
license-file = "..."

5.2.2依赖的详细配置:

# 注意,此处的cfg可以使用not、any、all等操作符任意组合键值对。
# 并且此用法仅支持cargo 0.9.0(rust 1.8.0)以上版本。
# 如果是windows平台,则需要此依赖。
[target.'cfg(windows)'.dependencies]
winhttp = "0.4.0"

[target.'cfg(unix)'.dependencies]
openssl = "1.0.1"

#如果是32位平台,则需要此依赖。
[target.'cfg(target_pointer_width = "32")'.dependencies]
native = { path = "native/i686" }

[target.'cfg(target_pointer_width = "64")'.dependencies]
native = { path = "native/i686" }

# 另一种写法就是列出平台的全称描述
[target.x86_64-pc-windows-gnu.dependencies]
winhttp = "0.4.0"
[target.i686-unknown-linux-gnu.dependencies]
openssl = "1.0.1"

# 如果使用自定义平台,请将自定义平台文件的完整路径用双引号包含
[target."x86_64/windows.json".dependencies]
winhttp = "0.4.0"
[target."i686/linux.json".dependencies]
openssl = "1.0.1"
native = { path = "native/i686" }
openssl = "1.0.1"
native = { path = "native/x86_64" }

# [dev-dependencies]段落的格式等同于[dependencies]段落,
# 不同之处在于,[dependencies]段落声明的依赖用于构建软件包,
# 而[dev-dependencies]段落声明的依赖仅用于构建测试和性能评估。
# 此外,[dev-dependencies]段落声明的依赖不会传递给其他依赖本软件包的项目
[dev-dependencies]
iron = "0.2"

5.2.3 自定义编译器配置

cargo内置五种编译器调用模板,分别为dev、release、test、bench、doc,分别用于定义不同类型生成目标时的编译器参数,如果我们自己想改变这些编译模板,可以自己定义相应字段的值.

# 开发模板, 对应`cargo build`命令
[profile.dev]
opt-level = 0  # 控制编译器的 --opt-level 参数,也就是优化参数
debug = true   # 控制编译器是否开启 `-g` 参数
rpath = false  # 控制编译器的 `-C rpath` 参数
lto = false    # 控制`-C lto` 参数,此参数影响可执行文件和静态库的生成,
debug-assertions = true  # 控制调试断言是否开启
codegen-units = 1 # 控制编译器的 `-C codegen-units` 参数。注意,当`lto = true`时,此字段值被忽略

# 发布模板, 对应`cargo build --release`命令
[profile.release]
opt-level = 3
debug = false
rpath = false
lto = false
debug-assertions = false
codegen-units = 1

# 测试模板,对应`cargo test`命令
[profile.test]
opt-level = 0
debug = true
rpath = false
lto = false
debug-assertions = true
codegen-units = 1

# 性能评估模板,对应`cargo bench`命令
[profile.bench]
opt-level = 3
debug = false
rpath = false
lto = false
debug-assertions = false
codegen-units = 1

# 文档模板,对应`cargo doc`命令
[profile.doc]
opt-level = 0
debug = true
rpath = false
lto = false
debug-assertions = true
codegen-units = 1

5.2.4 feature段落

[features]段落中的字段被用于条件编译选项或者是可选依赖。例如:

[package]
name = "awesome"

[features]
# 此字段设置了可选依赖的默认选择列表,
# 注意这里的"session"并非一个软件包名称,
# 而是另一个featrue字段session
default = ["jquery", "uglifier", "session"]

# 类似这样的值为空的feature一般用于条件编译,
# 类似于`#[cfg(feature = "go-faster")]`。
go-faster = []

# 此feature依赖于bcrypt软件包,
# 这样封装的好处是未来可以对secure-password此feature增加可选项目。
secure-password = ["bcrypt"]

# 此处的session字段导入了cookie软件包中的feature段落中的session字段
session = ["cookie/session"]

[dependencies]
# 必要的依赖
cookie = "1.2.0"
oauth = "1.1.0"
route-recognizer = "=2.1.0"

# 可选依赖
jquery = { version = "1.0.2", optional = true }
uglifier = { version = "1.5.3", optional = true }
bcrypt = { version = "*", optional = true }
civet = { version = "*", optional = true }

如果其他软件包要依赖使用上述awesome软件包,可以在其描述文件中这样写:

[dependencies.awesome]
version = "1.3.5"
default-features = false # 禁用awesome 的默认features
features = ["secure-password", "civet"] # 使用此处列举的各项features

使用features时需要遵循以下规则:
1.feature名称在本描述文件中不能与出现的软件包名称冲突
2.除了default feature,其他所有的features均是可选的
3.features不能相互循环包含
4.开发依赖包不能包含在内

  1. features组只能依赖于可选软件包
    features的一个重要用途就是,当开发者需要对软件包进行最终的发布时,在进行构建时可以声明暴露给终端用户的features,这可以通过下述命令实现:
$ cargo build --release --features "shumway pdf"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342