从Rust到Ruby

Hacker News Top 新闻

摘要

开发人员描述使用LLM将一个15,000行的Rust Web应用转换为Ruby on Rails,发现Ruby版本明显更短,并评估了开发速度、安全性和可测试性方面的权衡。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/05/27 03:59

# 从 Rust 到 Ruby 来源:https://xlii.space/eng/from-rust-to-ruby/ 2026\-05\-26 谁会干这种事?!显然:是我。我有个用 Rust 写的个人项目。不算大,总共大概 3 万行代码。Rust 很啰嗦,所以其实也没多厉害。我把它搁置了一段时间,转而玩本地推理、LLM、写 Agent,然后我的注意力被 Ruby 吸引了。已经好一阵子了。所以我得四处看看,提醒自己 Ruby 和 Ruby on Rails 现在在做什么。它们做得相当不错。有一些类型方面的尝试(Sorbet),语言本身一如既往地简洁。然后我就有了这个想法……但首先得做个介绍: 在我的 Rust 应用里,有一个独立的 crate,基本上是一个用 Tera 和 Axum 写的 web 应用。总共 14,943 行 Rust 代码,编译时间大约 10 秒(可能代码不大,但它拖拽了整个宇宙作为依赖),然后是非常重的端到端测试,涉及启动 Playwright 以及(因为几乎无法 mock)一个独立的数据库命名空间和 mock 服务(还有一个非常特殊的 internal-api crate,允许 Playwright 以无头模式与应用交互……)。所以我想:“嗯,不知道能不能让我的本地 Qwen3.6 做一次一次性转换”。但在此之前,我先做了调研。我问了几个实例,分析这个项目在复杂性、稳定性、可测试性等方面的得失,虽然(显然)稳定性会下降(Ruby 没有类型),但也没那么糟糕(Sorbet 在 Ruby 里有类型!)。 ``` ┌─────────────────────────────────┬──────────────────┬───────┬────────────────┐ │ 领域 │ Rust/Axum/Diesel │ Rails │ Rails + Sorbet │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 适合单人开发 │ 60 │ 90 │ 85 │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 开发速度 │ 40 │ 90 │ 75 │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 安全性 │ 95 │ 55 │ 80 │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 开发复杂度 │ 70 │ 90 │ 75 │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 性能 │ 95 │ 50 │ 50 │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 样板代码 │ 30 │ 85 │ 80 │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 端到端测试可测试性 │ 40 │ 75 │ 75 │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 单元测试可测试性 │ 20 │ 90 │ 90 │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 集成测试可测试性 │ 30 │ 85 │ 85 │ ├─────────────────────────────────┼──────────────────┼───────┼────────────────┤ │ 总和 │ 480 │ 710 │ 695 │ └─────────────────────────────────┴──────────────────┴───────┴────────────────┘ ``` 所以最后看起来,如果这个应用是 Ruby on Rails 写的,我的结果会好 *(舔手指,转向风)* 1.47 倍。有意思。 ![ai 生成的 Bugs Bunny 说 i-i-i-i-i-i-nteresting 的图片](image-url-placeholder) 我本地有台 4090 Ti(AI 热潮前买的用来玩游戏)上跑着 LLM1 — 我是个自由人,拥有无限 token2。所以我想:放马过来!因为这个项目比较小,转换花了大约 30 分钟。我不知道它能不能运行,因为我还没试过。但我检查了一件事,然后惊恐地盯着: ``` $ fd . -e rs -uu | xargs cat | wc -l 14943 $ fd . -e rb -uu | xargs cat | wc -l 3322 ``` ![ai 生成的 Bugs Bunny 赢得一级方程式赛车,带有巨大 77% 气球](image-url-placeholder) 没错,各位!代码行数减少了 77%;每行 Ruby 代码对应 4.49 行 Rust 代码。我浏览了 Ruby 代码,看起来……还行。可能有一些 bug(没有兔子),但就我过时的眼光来看,代码干净且符合惯用写法。我准备进一步检查,带着以下几点想法: - 我可以使用 Agents 添加类型,所以类型安全可能得到缓解 - Ruby/Rails 基本上是电池加厨房水槽全包,这比 3GiB 的编译依赖强多了 - 测试会*容易得多* ``` VCR.use_cassette("llm_call") do result = LlmClient.match(entry, data_list) expect(result.results.size).to eq(data_list.size) end ``` 对比 ``` #[derive(Debug)] pub struct MockProvider { responses: Arc<RwLock<Vec<Response>>>, call_count: Arc<AtomicUsize>, } impl Default for MockProvider { fn default() -> Self { Self { responses: Arc::new(RwLock::new(vec![Response::default()])), call_count: Arc::new(AtomicUsize::new(0)), } } } impl MockProvider { pub fn new(responses: Vec<Response>) -> Self { Self { responses: Arc::new(RwLock::new(responses)), call_count: Arc::new(AtomicUsize::new(0)), } } } #[async_trait] impl Provider for MockProvider { async fn match(&self, entry: &Entry, data_list: &[Data]) -> Result<MatchResult> { self.call_count.fetch_add(1, Ordering::SeqCst); let responses = self.responses.read().await; Ok(MatchResult { results: responses.clone() }) } } #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_mock_provider_returns_expected_results() { let expected = vec![Response::default()]; let provider = MockProvider::new(expected.clone()); let result = provider.match(&Entry::default(), &[]).await.unwrap(); assert_eq!(result.results, expected); assert_eq!(provider.call_count.load(Ordering::SeqCst), 1); } } ``` ……你懂那个*氛围*,对吧?做自己项目的好处是,你可以做疯狂的决策,而我会*非常仔细地*审视这个决定。

相似文章

从Go迁移到Rust

Hacker News Top

一份为Go开发者迁移到Rust编写的全面指南,专注于后端服务,对比正确性、运行时和人体工程学方面的权衡,并提供关于渐进式迁移的实用建议。

为什么多年来 Ruby 依然让人有家的感觉

Lobsters Hottest

作者回顾了使用 Ruby 的 15 年经历,称赞了其隐藏特性,如 refinements、delegation 以及新的 ZJIT JIT 编译器,并指出 Ruby 搭配 ZJIT 正在缩小与 Go 和 Rust 等更快语言的性能差距。