Rust 編譯時特性

Rust 選擇編譯時特性的能力可以提高代碼的性能、大小、可維護性、安全性和可移植性。

下面是一些關於爲什麼在依賴項時應該主動使用編譯時特性,並將這些特性提供給其他庫用戶。

性能

在 Rust 中使用特性標誌可以提高結果代碼的性能,通過只包含特定應用程序所需的代碼,可以避免因使用不必要的代碼所帶來的開銷。

儘管有編譯器優化來刪除死代碼,但這仍然可以產生更快、更高效的程序 (並使編譯器的工作更輕鬆)。

大小

生成的二進制文件的總體大小受到包含的依賴項以及如何使用它們的影響。特性選擇可以幫助生成的二進制文件更小,有利於需要分佈或部署到資源受限環境的應用程序。

可維護性

在等待上游庫更新的過程中,我刪除了本地項目的功能,該項目再次構建正常。這意味着您可以通過允許開發人員有選擇地包含或排除特定功能來提高 Rust 代碼的可維護性。

安全性

從統計學上講,依賴的代碼越多,出現安全問題的幾率就越高。僅依賴於你所需要的功能來降低安全問題的幾率是基於設計的安全思維,而 “分塊” 提供自身的庫有助於實現這一點。

還有一些方法可以根據你對實現的安全性的滿意程度來選擇相同功能的不同實現。例如,你可能更喜歡 Rust 原生 TLS 實現,而不是基於 c 的 TLS 實現,因爲 Rust 是一種安全的語言,而且一些像 Reqwest 這樣的庫也提供了 TLS 後端選擇。

可移植性

作爲一種編譯語言,特性標誌的一個重要方面是提高代碼的可移植性。你可以有選擇地包括或排除特定的功能,以使你的代碼在不同的平臺和環境中更易於移植。

例子

要爲特定的 crate 啓用特定的特性標誌,你可以在 crate 的 Cargo.toml 文件中使用 default-features = false 和 features 屬性。例如:

[dependencies]
my-crate = { default-features = false, features = ["my-feature"] }

要爲特定的代碼段啓用特性標誌,可以使用 #[cfg(feature = "my-feature")] 屬性。例如:

1#[cfg(feature = "my-feature")]
2fn my_function() {
3    // Code that is only included when the "my-feature" flag is enabled
4}

要爲特定模塊啓用特性標誌,可以在 mod 聲明中使用 #[cfg(feature = "my-feature")] 屬性。例如:

1#[cfg(feature = "my-feature")]
2mod my_module {
3    // Code that is only included when the "my-feature" flag is enabled
4}

要啓用帶有派生的特定結構或枚舉的特性標誌,可以使用 #[cfg_attr(feature = "my-feature", derived(…))] 屬性。例如:

1#[cfg_attr(feature = "my-feature", derive(Debug, PartialEq))]
2struct MyStruct {
3    // Fields and methods that are only included when the "my-feature" flag is enabled
4}

以下是如何啓用或禁用對特定平臺的支持:

1#[cfg(target_os = "linux")]
2mod linux_specific_code {
3    // Linux-specific code goes here...
4}

以及如何啓用或禁用 trait 的特定實現:

1#[cfg(feature = "special_case")]
2impl MyTrait for MyType {
3    // Implementation of trait for special case goes here...
4}

如何啓用或禁用特定的測試用例:

1#[cfg(feature = "expensive_tests")]
2#[test]
3fn test_expensive_computation() {
4    // Test that performs expensive computation goes here...
5}

下面是啓用或禁用特定基準測試的代碼:

1#[cfg(feature = "long_benchmarks")]
2#[bench]
3fn bench_long_running_operation(b: &mut Bencher) {
4    // Benchmark for a long-running operation goes here...
5}

要在設置多個標誌時啓用一個特性,可以使用 #[cfg(all(feature1, feature2,…))] 屬性。例如,只在 my_feature1 和 my_feature2 標誌都設置時啓用 my_function():

1#[cfg(all(feature = "my_feature1"feature = "my_feature2"))]
2fn my_function() {
3    // code for my_function
4}

要只在設置多個標誌之一時啓用一個特性,可以使用 #[cfg(any(feature1, feature2,…))] 屬性。例如,當設置 my_feature1 或 my_feature2 標誌時啓用 my_function():

1#[cfg(any(feature = "my_feature1"feature = "my_feature2"))]
2fn my_function() {
3    // code for my_function
4}

相同的模塊,但指向不同的實現路徑,然後從該模塊中取出一個函數公開:

 1//! Signal monitor
 2#[cfg(unix)]
 3#[path = "unix.rs"]
 4mod imp;
 5#[cfg(windows)]
 6#[path = "windows.rs"]
 7mod imp;
 8#[cfg(not(any(windows, unix)))]
 9#[path = "other.rs"]
10mod imp;
11pub use self::imp::create_signal_monitor;

在下面的例子中,標準庫允許你選擇任何你能想到的分配器,因爲分配器有一個定義良好的接口,並且不需要你做任何工作:

 1//! Memory allocator
 2#[cfg(feature = "jemalloc")]
 3#[global_allocator]
 4static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
 5#[cfg(feature = "tcmalloc")]
 6#[global_allocator]
 7static ALLOC: tcmalloc::TCMalloc = tcmalloc::TCMalloc;
 8#[cfg(feature = "mimalloc")]
 9#[global_allocator]
10static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
11#[cfg(feature = "snmalloc")]
12#[global_allocator]
13static ALLOC: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc;
14#[cfg(feature = "rpmalloc")]
15#[global_allocator]
16static ALLOC: rpmalloc::RpMalloc = rpmalloc::RpMalloc

在下面的例子中,你會看到如何讓你的應用按需要的功能進行 “分層”:

1//! Service launchers
2pub mod genkey;
3#[cfg(feature = "local")]
4pub mod local;
5#[cfg(feature = "manager")]
6pub mod manager;
7#[cfg(feature = "server")]
8pub mod server;

本文翻譯自:

https://betterprogramming.pub/compile-time-feature-flags-in-rust-why-how-when-129aada7d1b3

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/yViKxPT6shFAK5d4YxUzhw