作者 | Josh Mo
譯者 | 核子可樂
策劃 | 李冬梅
如果大家已經(jīng)擁有一定的 Rust Web 開發(fā)經(jīng)驗,應(yīng)該聽說過在前端 Web 開發(fā)上用 Rust(通過 WASM)還是用 JAVA 這個充滿爭議性的話題。不少人旗幟鮮明表示反對,認(rèn)為 Rust“不適合生產(chǎn)”,而且速率“比 Java 還慢”。
這種說法也有道理:從歷史上看,因為 WASM 無法訪問 DOM,所以從 Java 調(diào)用 WASM 確實會產(chǎn)生額外開銷。但目前這方面的影響已經(jīng)很小,基準(zhǔn)數(shù)據(jù)顯示,像 Leptos 和 Dioxus 這樣的 Rust WASM 框架(底層使用 Sledgehammer,屬于速度前三甲級別的 Java 框架)在性能上已經(jīng)優(yōu)于 React 和 Vue 等大部分 JS 框架。感興趣的朋友可以參考原始基準(zhǔn)測試。
如圖片所見,各框架按性能排序分別為原始 Java、Sledgehammer(Dioxus 的底層引擎)、wasm-bindgen(允許 WASM 模塊和 Java 實現(xiàn)互操作的庫)、Solid.js ,Vue 和 RxJS,之后是 Leptos、Dioxus、LitJS,接下來是 Sycamore……排在最末的才是 Vue 和 React(還有 Yew)。很明顯,其中一些 Rust 前端框架甚至比最流行的 Java 框架性能還好。千萬別抬杠說也可以不用框架,直接編寫純 Java 代碼——確實可以,但這明顯偏離本文討論的主題了。
TechEmpower 發(fā)布的后端性能基準(zhǔn)測試:
在前 10 大后端框架中,有 5 個是用 Rust 編寫的。很明顯,Rust 在后端框架領(lǐng)域占據(jù)著突出的優(yōu)勢,甚至能與 C++ 正面較量。有人可能會說 Rust 用作后端服務(wù)有點太過了——但它確實能帶來更高性能,占用的內(nèi)存更小、服務(wù)的運行穩(wěn)定性更好、引發(fā)崩潰的可能性也更低。這些都是不容低估的重要因素,畢竟從企業(yè)的角度來看,盡可能節(jié)約成本永遠(yuǎn)都是高優(yōu)先級事項。
但也必須承認(rèn),在選擇新框架時,速度和常規(guī)性能往往并不足以構(gòu)成綜合決策的充分因素。開發(fā)者體驗如何、錯誤處理功能是否強大、怎樣解決 SSR 問題等也都非常重要。要想做出明智的最終選擇,必須先為這些問題找到合理答案。幸運的是,Rust 同樣是有備而來。
開發(fā)者體驗
不管大家主觀判斷如何,在 Web 開發(fā)方面,Rust 有著相對寬松的使用要求。其中很多代碼的樣式上跟 React 等 Web 框架中的 Java 組件非常相似——比如 Leptos(一款 Rust Web 框架)中的組件代碼:
use leptos::*;#[component]pub fn SimpleCounter(cx: Scope, initial_value: i32) -> impl IntoView{// create a reactive signal with the initial valuelet(value, set_value) = create_signal(cx, initial_value);
// create event handlers for our buttons// note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures// (for reference: closures are like anonymous/arrow functions in Java)letclear = move |_| set_value(0);letdecrement = move |_| set_value.update(|value| *value -= 1);letincrement = move |_| set_value.update(|value| *value += 1);
// create user interfaces with the declarative `view!` macroview! {cx,<div><button on:click=clear>"Clear"</button><button on:click=decrement>"-1"</button><span>"Value: "{value} "!"</span><button on:click=increment>"+1"</button></div>}}
// Easy to use with Trunk (trunkrs.dev) or with a simple wasm-bindgen setuppub fn main() {mount_to_body(|cx| view! { cx, <SimpleCounterinitial_value=3/> })}
可以看到,這些代碼其實跟 JSX 區(qū)別不大,最大的不同就是該組件不返回任何內(nèi)容,而是用 Rust 宏來渲染 html。其 main 函數(shù)類似于 React、Vue 乃至其他 JS 框架當(dāng)中作用于 root 文件的 index.js 腳本。再來看另一個來自 Dioxus 的例子:
// An example of a navbarfn navbar(cx: Scope) -> Element {cx.render(rsx! {ul {// NEWLink { to: "/", "Home"}br {}Link { to: "/blog", "Blog"}}})}// An example of using URL parametersfn get_blog_post(id: &str) -> String{match id {"foo"=> "Welcome to the foo blog post!".to_string(),"bar"=> "This is the bar blog post!".to_string(),id=>format!("Blog post '{id}' does not exist!")}
可以看到,RSX(相當(dāng)于 Dioxus 中的 React JSX)的編寫非常簡單,甚至可能比使用 Leptos 還簡單一些。而且很明顯,React 的組件設(shè)計理念已經(jīng)超越了特定編程語言,在 Rust 這邊也已經(jīng)有所體現(xiàn)。大家甚至可以把這些函數(shù)跟單元結(jié)構(gòu)體(unit structs)結(jié)合起來,為各種函數(shù)提供命名空間,這樣就能實現(xiàn)對 API 調(diào)用之類的捆綁了,例如:
// this is a unit structpub struct APICalls;// we can implement the unit struct to bundle functions under it// like so:Impl APICalls {pub asyncfn get_dog_api_data() -> Json<Dog> {... some code here// this should probably return some json data}pub asyncfn get_cat_api_data() -> Json<Cat> {... some code here// this should probably return some json data}}
fn navbar (cx: Scope) -> Element {// now we can call the data like this, or something similarletdogs = APICalls::get_dog_api_data().await;}
如大家所見,哪怕只是稍稍觸及 Rust 的淺表層次,也已經(jīng)能夠獲得相當(dāng)不錯的開發(fā)效果。而且真正讓人眼前一亮的,還要數(shù) Rust 的錯誤處理機制,這也是其優(yōu)于 Java 甚至是 Type 的關(guān)鍵亮點之一。通常,如果使用 Type 進行編碼,我們只有兩個選擇:類型檢查和 try-catch 塊。但對于擁有一定開發(fā)經(jīng)驗的朋友們來說,不斷把代友打包到 try-catch 塊中仍然有其隱患。畢竟 Type 仍可被編譯為 Java,所以一旦不小心就會引發(fā)跟 JS 相關(guān)的問題(CJS 和 ECMA 兼容問題,運行時內(nèi)隨時可能出現(xiàn)的隨機錯誤等)。
下面來看看 Rust 的基本錯誤處理機制:
asyncfn foo() -> Result<String, String>{letbar = String::from("foobar!");// return is implicit, no need to write "return"match bar.trim() {"foobar!"=> Ok(bar),_ => Err("Was not foobar!".to_string())}}#[tokio::main]fn main() -> Result<String, String> {letOk(res) = foo().await else{returnErr("Was not foobar :(".to_string());}
println!("The string was: {res}!");}
這里展示了兩個示例:我們可以使用基礎(chǔ)模式匹配來確定字符串是什么,如果結(jié)果匹配則返回 OK;如果屬于其他內(nèi)容(會加注下劃線),則只返回一個具有 String 類型的錯誤(也會提示 std::error::Error -,我們可以將其作為錯誤類型來處理)。我們還可以聲明一個變量,要求該變量必須是實際的 Result 類型,否則執(zhí)行其他操作(在示例中為提前返回)。之后,我們就可以使用 res 本體了,因為它將被聲明為 Result 中包含的值。
生態(tài)系統(tǒng)
雖然 Java 的生態(tài)系統(tǒng)(Node/npm)要比 Rust 龐大得多,但 Rust 陣營也完全能夠滿足大多數(shù)項目的需求。Rust 目前對數(shù)據(jù)庫、redis 和 Web 應(yīng)用程序中所需的各種服務(wù)都提供良好支持,不管用哪種編程語言都能使用。
如果您打算構(gòu)建 SaaS,Rust 正好準(zhǔn)備了幾乎包羅萬象的工具箱:用于 SMTP 的 lettre、用于 Stripe 支付的 async-stripe,用于處理社交網(wǎng)絡(luò)賬戶登錄的 OAuth 回調(diào) oauth2,用于數(shù)據(jù)庫(甚至是 airtable)的 SQLx(如果傾向于對象關(guān)系映射,還有 Diesel 或 SeaORM 可以選擇)。當(dāng)然,還有用于 GPT-3 的 openai_api。在 SaaS 投入運行之后,Rust 甚至支持用于 RabbitMQ 的 lapin 和用于 Kafka 的 rs-rdkafka。由此看來,如果大家想開發(fā)一項堅如磐石的高性能服務(wù),Rust 的表現(xiàn)完全可以跟 Java 正面抗衡。
根據(jù)個人經(jīng)驗,我發(fā)現(xiàn) cargo 在對接各種工具時表現(xiàn)突出。以 clippy 為例,這是一款無需初始化就能使用的出色工具程序,只要輸入 cargo clippy 即可啟用,它能檢測出不必要的借用等部分、幫助我們快速優(yōu)化代碼。更重要的是,如果需要把一個項目中的配置遷移至另一項目,也可以直接在根目錄下創(chuàng)建一個 clippy.toml 文件并隨意加以配置。
由于 Rust 本身并不是普及度最高的 Web 編程語言,所以生態(tài)系統(tǒng)中各廠商對它的支持態(tài)度可能沒那么積極,比如開放相應(yīng)服務(wù) API。但因為大多數(shù)服務(wù) API 采取的都是 HTTP REST Web 服務(wù)的形式,所以 Rust 也能用得起來,大家還可以使用 reqwest 等工具檢索自己需要的數(shù)據(jù)。
部 署
在部署方面,Shuttle 是迄今為止最簡單的 Rust 部署方法。后端部署確實要麻煩一點,要么需要鼓搗配置文件、要么通過網(wǎng)站上的 GUI 添加環(huán)境變量來接入需要使用的服務(wù),或者是提供相應(yīng)的靜態(tài)文件。
Shuttle 的另一個優(yōu)點就是采取基礎(chǔ)設(shè)施即代碼的實現(xiàn)理念,可以通過代碼注釋快速上手。只需簡單通過 Rust 宏在 main 函數(shù)中聲明,大家就能避免親自動手鼓搗配置文件。我們可以借此交付數(shù)據(jù)庫并支持靜態(tài)文件,從能夠編譯為靜態(tài)資產(chǎn)的 Next.js、React 等 JS 框架處添加編譯前端,例如:
// main.rs#[shuttle_runtime::main]pub async fn axum (#[shuttle_shared_db::Postgres] postgres: PgPool,#[shuttle_secrets::Secrets] secrets: SecretStore,#[shuttle_static_folder] static: PathBuf) -> shuttle_axum::ShuttleAxum {// carry out database migrations (this assumes migrations are idempotent)sqlx::migrate!().run(&postgres).await.expect("Migrations failed :(");let hello_world = secrets.get("MY_VARIABLE").expect("Is MY_VARIABLE set in Secrets.toml?");
// Make a router serving API routes that require a DB connectionlet api_router = create_api_router(postgres);
// Add a compiled frontend (like e.g. from Next.js, React, Vue etc) to the routerlet router = Router::new().nest("/api", api_router).nest_service("/", get_service(ServeDir::new(static).handle_error(handle_error));
// Rust returns implicitly so writing "return" is not requiredOk(router.into())}
總 結(jié)
綜上所述,Rust 無疑是一款值得用于 Web 開發(fā)的優(yōu)秀語言。憑借著內(nèi)存占用小、性能水平高、正常運行時間長和運維成本低等優(yōu)勢,Rust 將幫助您在前端領(lǐng)域節(jié)約下寶貴的時間和金錢。
原文鏈接:
https://joshuamo876.bearblog.dev/can-rust-beat-java-in-2023/
聲明:本文為 InfoQ 翻譯,未經(jīng)許可禁止轉(zhuǎn)載。
點擊底部閱讀原文訪問 InfoQ 官網(wǎng),獲取更多精彩內(nèi)容!
今日好文推薦
比Python/ target=_blank class=infotextkey>Python快35000倍!LLVM&Swift之父宣布全新編程語言Mojo:編程被顛覆了
拼多多回應(yīng)將總部從中國遷至愛爾蘭;微軟Bing爆炸級更新,文生圖原生支持中文;75歲人工智能教父離職谷歌,痛悔畢生工作| Q資訊
谷歌、OpenAI 都白干,開源才是終極贏家!谷歌內(nèi)部文件泄露:欲借開源打敗 OpenAI
谷歌用機器人大規(guī)模刪除代碼:二十多年積累了數(shù)十億行,已刪除5%C++代碼