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"
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。