作者 | Josh Mo
译者 | 核子可乐
策划 | 李冬梅
如果大家已经拥有一定的 Rust Web 开发经验,应该听说过在前端 Web 开发上用 Rust(通过 WASM)还是用 JAVA 这个充满争议性的话题。不少人旗帜鲜明表示反对,认为 Rust“不适合生产”,而且速率“比 Java 还慢”。
这种说法也有道理:从历史上看,因为 WASM 无法访问 DOM,所以从 Java 调用 WASM 确实会产生额外开销。但目前这方面的影响已经很小,基准数据显示,像 Leptos 和 Dioxus 这样的 Rust WASM 框架(底层使用 Sledgehammer,属于速度前三甲级别的 Java 框架)在性能上已经优于 React 和 Vue 等大部分 JS 框架。感兴趣的朋友可以参考原始基准测试。
如图片所见,各框架按性能排序分别为原始 Java、Sledgehammer(Dioxus 的底层引擎)、wasm-bindgen(允许 WASM 模块和 Java 实现互操作的库)、Solid.js ,Vue 和 RxJS,之后是 Leptos、Dioxus、LitJS,接下来是 Sycamore……排在最末的才是 Vue 和 React(还有 Yew)。很明显,其中一些 Rust 前端框架甚至比最流行的 Java 框架性能还好。千万别抬杠说也可以不用框架,直接编写纯 Java 代码——确实可以,但这明显偏离本文讨论的主题了。
TechEmpower 发布的后端性能基准测试:
在前 10 大后端框架中,有 5 个是用 Rust 编写的。很明显,Rust 在后端框架领域占据着突出的优势,甚至能与 C++ 正面较量。有人可能会说 Rust 用作后端服务有点太过了——但它确实能带来更高性能,占用的内存更小、服务的运行稳定性更好、引发崩溃的可能性也更低。这些都是不容低估的重要因素,毕竟从企业的角度来看,尽可能节约成本永远都是高优先级事项。
但也必须承认,在选择新框架时,速度和常规性能往往并不足以构成综合决策的充分因素。开发者体验如何、错误处理功能是否强大、怎样解决 SSR 问题等也都非常重要。要想做出明智的最终选择,必须先为这些问题找到合理答案。幸运的是,Rust 同样是有备而来。
开发者体验
不管大家主观判断如何,在 Web 开发方面,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 区别不大,最大的不同就是该组件不返回任何内容,而是用 Rust 宏来渲染 html。其 main 函数类似于 React、Vue 乃至其他 JS 框架当中作用于 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(相当于 Dioxus 中的 React JSX)的编写非常简单,甚至可能比使用 Leptos 还简单一些。而且很明显,React 的组件设计理念已经超越了特定编程语言,在 Rust 这边也已经有所体现。大家甚至可以把这些函数跟单元结构体(unit structs)结合起来,为各种函数提供命名空间,这样就能实现对 API 调用之类的捆绑了,例如:
// 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 的浅表层次,也已经能够获得相当不错的开发效果。而且真正让人眼前一亮的,还要数 Rust 的错误处理机制,这也是其优于 Java 甚至是 Type 的关键亮点之一。通常,如果使用 Type 进行编码,我们只有两个选择:类型检查和 try-catch 块。但对于拥有一定开发经验的朋友们来说,不断把代友打包到 try-catch 块中仍然有其隐患。毕竟 Type 仍可被编译为 Java,所以一旦不小心就会引发跟 JS 相关的问题(CJS 和 ECMA 兼容问题,运行时内随时可能出现的随机错误等)。
下面来看看 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}!");}
这里展示了两个示例:我们可以使用基础模式匹配来确定字符串是什么,如果结果匹配则返回 OK;如果属于其他内容(会加注下划线),则只返回一个具有 String 类型的错误(也会提示 std::error::Error -,我们可以将其作为错误类型来处理)。我们还可以声明一个变量,要求该变量必须是实际的 Result 类型,否则执行其他操作(在示例中为提前返回)。之后,我们就可以使用 res 本体了,因为它将被声明为 Result 中包含的值。
生态系统
虽然 Java 的生态系统(Node/npm)要比 Rust 庞大得多,但 Rust 阵营也完全能够满足大多数项目的需求。Rust 目前对数据库、redis 和 Web 应用程序中所需的各种服务都提供良好支持,不管用哪种编程语言都能使用。
如果您打算构建 SaaS,Rust 正好准备了几乎包罗万象的工具箱:用于 SMTP 的 lettre、用于 Stripe 支付的 async-stripe,用于处理社交网络账户登录的 OAuth 回调 oauth2,用于数据库(甚至是 airtable)的 SQLx(如果倾向于对象关系映射,还有 Diesel 或 SeaORM 可以选择)。当然,还有用于 GPT-3 的 OpenAI_api。在 SaaS 投入运行之后,Rust 甚至支持用于 RabbitMQ 的 lapin 和用于 Kafka 的 rs-rdkafka。由此看来,如果大家想开发一项坚如磐石的高性能服务,Rust 的表现完全可以跟 Java 正面抗衡。
根据个人经验,我发现 cargo 在对接各种工具时表现突出。以 clippy 为例,这是一款无需初始化就能使用的出色工具程序,只要输入 cargo clippy 即可启用,它能检测出不必要的借用等部分、帮助我们快速优化代码。更重要的是,如果需要把一个项目中的配置迁移至另一项目,也可以直接在根目录下创建一个 clippy.toml 文件并随意加以配置。
由于 Rust 本身并不是普及度最高的 Web 编程语言,所以生态系统中各厂商对它的支持态度可能没那么积极,比如开放相应服务 API。但因为大多数服务 API 采取的都是 HTTP REST Web 服务的形式,所以 Rust 也能用得起来,大家还可以使用 reqwest 等工具检索自己需要的数据。
部 署
在部署方面,Shuttle 是迄今为止最简单的 Rust 部署方法。后端部署确实要麻烦一点,要么需要鼓捣配置文件、要么通过网站上的 GUI 添加环境变量来接入需要使用的服务,或者是提供相应的静态文件。
Shuttle 的另一个优点就是采取基础设施即代码的实现理念,可以通过代码注释快速上手。只需简单通过 Rust 宏在 main 函数中声明,大家就能避免亲自动手鼓捣配置文件。我们可以借此交付数据库并支持静态文件,从能够编译为静态资产的 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())}
总 结
综上所述,Rust 无疑是一款值得用于 Web 开发的优秀语言。凭借着内存占用小、性能水平高、正常运行时间长和运维成本低等优势,Rust 将帮助您在前端领域节约下宝贵的时间和金钱。
原文链接:
https://joshuamo876.bearblog.dev/can-rust-beat-java-in-2023/
声明:本文为 InfoQ 翻译,未经许可禁止转载。
点击底部阅读原文访问 InfoQ 官网,获取更多精彩内容!
今日好文推荐
比Python/ target=_blank class=infotextkey>Python快35000倍!LLVM&Swift之父宣布全新编程语言Mojo:编程被颠覆了
拼多多回应将总部从中国迁至爱尔兰;微软Bing爆炸级更新,文生图原生支持中文;75岁人工智能教父离职谷歌,痛悔毕生工作| Q资讯
谷歌、OpenAI 都白干,开源才是终极赢家!谷歌内部文件泄露:欲借开源打败 OpenAI
谷歌用机器人大规模删除代码:二十多年积累了数十亿行,已删除5%C++代码