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包括:
- extern crate声明
- use声明
- 模块(模块是一个Item的容器)
- 函数
- type定义
- 结构体定义
- 枚举类型定义
- 常量定义
- 静态变量定义
- Trait定义
- 实现(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.开发依赖包不能包含在内
- features组只能依赖于可选软件包
features的一个重要用途就是,当开发者需要对软件包进行最终的发布时,在进行构建时可以声明暴露给终端用户的features,这可以通过下述命令实现:
$ cargo build --release --features "shumway pdf"