新Rust程式設計師需要學習的9個功能

banq發表於2022-02-07

Rust 是一種相當龐大且複雜的程式語言,具有許多特性。但我有一個好訊息:不到20% 的功能會給你帶來超過 80% 的結果。
以下是我認為在開始 Rust 時必須學習的功能。
 

列舉
Enums(也叫代數資料型別)當然是新Rustaceans最喜歡的功能,因為它們是Result和Option的基礎。

enum Result<T, E> {
   Ok(T),
   Err(E),
}

pub enum Option<T> {
    None,
    Some(T),
}

列舉允許開發者安全地將其程式的所有可能狀態編碼到程式碼中,並在編譯時檢查他們是否忘記了某個情況。

#[derive(Debug, Clone, Copy)]
enum Platform {
    Linux,
    MacOS,
    Windows,
    Unknown,
}

impl fmt::Display for Platform {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Platform::Linux => write!(f, "Linux"),
            Platform::Macos => write!(f, "macOS"),
            // Compile time error! We forgot Windows and Unknown
        }
    }
}


 

非同步等待(async-await)
執行緒被設計用來並行化計算密集型任務。然而,現在很多應用程式(如網路掃描器或網路伺服器)都是I/O(輸入/輸出)密集型的,這意味著如果使用執行緒,我們的應用程式將花費大量時間來等待網路請求的完成,並使用超過必要的資源。

這些都是async-await所解決的問題,同時提供了一個很好的開發者體驗。

  

Traits
你可能需要切換多個共享相同行為的類似型別的具體實現。

例如,一個儲存驅動器。

struct FilesystemStorage {
  get() // ...
  put() // ...
  delete() // ...
}

struct S3Storage {
  get() // ...
  put() // ...
  delete() // ...
}


為此,我們使用traits,在其他語言中也稱為介面。

trait Storage {
  get() // ...
  put() // ...
  delete() // ...
}

impl Storage for FilesystemStorage {
  // ...
}

impl Storage for S3Storage {
  // ...
}


fn use_storage<S: Storage>(storage: S) {
  // ...
}

 

智慧指標
簡而言之,它們允許開發者避免生命週期管理,從而寫出更乾淨的程式碼。

它們也是traits obejcts的基礎,允許你在執行時選擇正確的實現(而不是用泛型編譯時)。

struct MyService {
  db: Arc<DB>,
  mailer: Arc<dyn drivers::Mailer>,
  storage: Arc<dyn drivers::Storage>,
  other_service: Arc<other::Service>,
}


  

Collections
Rust的標準庫的集合使用Rust編寫複雜的演算法和商業邏輯變得如此愉快。

let dedup_subdomains: HashSet<String> = subdomains.into_iter().collect();

 
迭代器
迭代器是一個使開發者能夠遍歷集合的物件。它們可以從標準庫的大多數集合中獲得。

fn filter() {
    let v = vec![-1, 2, -3, 4, 5].into_iter();

    let _positive_numbers: Vec<i32> = v.filter(|x: &i32| x.is_positive()).collect();
}


迭代器是懶惰的:如果它們不被消耗,它們就不會做任何事情。
 

組合器Combinators
組合器是一個非常有趣的話題。幾乎所有你在網際網路上找到的定義都會讓你的腦袋爆炸,因為它們提出的問題比回答的多。
因此,這裡是我的經驗性定義。組合器是簡化對某種型別T的操作的方法。它們有利於程式碼的函式性(方法鏈)風格。

let sum: u64 = vec![1, 2, 3].into_iter().map(|x| x * x).sum();


// Convert a `Result` to an `Option`
fn result_ok() {
    let _port: Option<String> = std::env::var("PORT").ok();
}

// Use a default `Result` if `Result` is `Err`
fn result_or() {
    let _port: Result<String, std::env::VarError> =
        std::env::var("PORT").or(Ok(String::from("8080")));
}

// Use a default value if empty, then apply a function
let http_port = std::env::var("PORT")
    .map_or(Ok(String::from("8080")), |env_val| env_val.parse::<u16>())?;

// Chain a function if `Result` is `Ok` or a different function if `Result` is `Err`
let master_key = std::env::var("MASTER_KEY")
    .map_err(|_| env_not_found("MASTER_KEY"))
    .map(base64::decode)??;


 

流Stream
流可以被粗略地定義為非同步async世界的迭代器iterator。
當你想對同一型別的專案序列進行非同步操作時,你應該使用它們,無論是網路套接字、檔案,還是長生命的HTTP請求。

任何太大的東西都不能放在記憶體中,因此應該被分割成小塊,或者可能稍後到達,但我們不知道什麼時候,或者這只是一個集合(例如Vec或HashMap),我們需要對其應用非同步操作。

它們還允許我們輕鬆地併發執行操作。

async fn compute_job(job: i64) -> i64 {
  // ...
}

#[tokio::main]
async fn main() {
    let jobs = 0..100;
    let concurrency = 42;

    stream::iter(jobs)
        .for_each_concurrent(concurrency, |job| compute_job(job)).await;
}

 

no_std
最後,Rust非常適用於嵌入式開發和shellcodes。因為這些環境不依賴於某個作業系統,所以你一般不能使用Rust的標準庫,而是需要使用核心庫。

對於這些用例,我們使用#![no_std]屬性。

#![no_std]
#![no_main]

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
fn _start() {
  // ...
}


 

相關文章