【CSDN 編者按】停止按照 JAVA 的方式編寫 Rust,這是我發現編寫 Rust 代碼的樂趣。
原文鏈接:https://jgayfer.com/dont-write-rust-like-java
作者 | James Gayfer譯者| 彎月
責編 | 夏萌
出品 | CSDN(ID:CSDNnews)
多年來,我一直對 Rust 很感興趣。類型安全、內存安全并且強調正確性。誰能不愛?
我在開發 Apollo(一款 Python/ target=_blank class=infotextkey>Python 應用)時遇到過很多錯誤,假如我使用的是 Rust,那么大多數錯誤都可以被 Rust 編譯器捕獲(雖然達不到百分百,但比例應該會很高)。一般來說,編譯器可以捕獲許多問題,而在使用動態語言(如 Python 或 Ruby)時,這些問題有可能進入生產環境,盡管并非所有編譯器都能做到這一點。類型安全非常棒,但 Rust 十分注重正確性,這才是最吸引我的地方。
我在工作中編寫了大量 Java 代碼。雖然 Java 不是我最喜歡的語言,但它的編譯時檢查很強大。在進行重大重構時,Java 沒有使用 Python 或Ruby 那么可怕。有了這樣的編譯器,遇到不正確或遺漏的導入語句,程序在運行時就會停止。雖然我們通常會通過測試來發現這些問題,但是將這些檢查融入到語言中還是很有必要的。
然而 Java 編譯器并不完美。它無法防止許多種錯誤,其中最令人頭疼的就是空引用。在Java 中幾乎所有東西都可以為 null,相關的錯誤直到運行時你才能發現。與之相反,Rust 擁有適當的結構來引導你處理未知值。當然,你可以選擇忽略此類提示,但編譯器會強制你做出深思熟慮的決定。
那么,Rust 是否比 Java 更好呢?Rust確實有許多讓我很喜歡的地方。Rust 的承諾對我來說非常有吸引力。但我的 Rust 之旅并不全是陽光和彩虹。盡管 Rust 與 Java 有相似之處,但二者并不一樣。直到停止按照 Java 的方式編寫 Rust,我才發現了編寫 Rust 代碼的樂趣。
一切必須是接口
對于 Java 開發人員(我就是這樣的開發人員)來說,一切都是接口,雖然這個說法不完全準確,但也有一定的道理。Java 中的接口使用起來很有趣。應用程序由小的工作單元組成,每個工作單元都不了解另一個工作單元的內部工作原理。建立這樣的依賴關系樹需要在前提付出不少努力,但一旦完成,就能擁有一支獨立服務的大軍供你使用。
然而,Rust 中沒有接口,有的是特征(trAIt)。這些特征在很多方面與 Java 中的接口很相似。然而,我們不應該將 Rust 中的一切都寫成特征。記住,Rust 的內存安全是一個很強大的功能。而代價是無法輕松“注入”實現特征的代碼。
上面的代碼無法編譯,因為編譯時無法確定 Named 的大小。為了解決這個問題,我們可以將這個特征放入Box ,這樣我們就可以指向堆上動態分配的內存(稱為 trait 對象)。Box 本身的大小已知,因此程序就可以編譯了。
Box不太方便使用,因此我不太喜歡這種模式。我會盡可能避開它們。我們可以使用泛型來指定 特征類型。
這兩種方式有何不同?初看之下結果是一樣的。實際上二者的差異在于動態調度與靜態調度。對于特征對象,具體的類型是在運行時解析的,而泛型的具體類型是在編譯時解析的。
實際上,這意味著只要我們可以在編譯時推斷所有類型,就可以不使用泛型。如果直到運行時才能推斷類型,則必須使用 Box。
所有權
所有權的問題依然存在。如果上述 Named 特征是應用程序中其他服務的必需依賴項,該怎么辦?我們是否需要創建一個主Named ,然后將 &Named 傳遞給每個依賴項,這樣就會引入生命周期?
還是說我們應該使用 Arc,這樣依賴服務就可以寫為 Arc<Box<dyn Named>>,從而允許并發訪問所擁有的資源?
兩種方法我都嘗試過了,雖然可行,但都不太理想,尤其是當應用程序中的每項服務都受到影響時。
純函數
將 Rust 當成純粹的面向對象語言并不合適。雖然我仍然像上面的例子一樣編寫“服務對象”,但只在必要時使用它們,實際上我更推薦純函數。
我們來考慮一個處理結賬事件的函數,該函數會更新系統中的客戶 ID。
雖然我們可以將這段處理編寫為一個服務,其中的 UserRepo 是一個注入值,但上面我們已經探討過了,這樣做會帶來一定的復雜性。此外,我們仍然可以輕松注入 UserRepo 的不同實現,例如提供不會訪問生產數據庫的實現,因此也沒有理由將其編寫為服務。缺點在于,我們的函數簽名可能會有點“繁忙”,不過這種程度的“痛苦”根本不算什么。
擁抱真正的 Rust
以前我深陷“Rust 語言很難”的誤區不能自拔。一個重要原因是我堅持按照其他語言的方式編寫 Rust 代碼。雖然汲取以往的經驗很重要,但擁抱 Rust 本身的慣用寫法對于掌握這門語言也很重要。只有轉變心態才能真正掌握 Rust。按照不適合的方式編寫 Rust 會讓我們陷入苦苦的掙扎,我們應該擁抱它本來的樣子。