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

Lobsters Hottest 新闻

摘要

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

<p><a href="https://lobste.rs/s/n1gytv/why_ruby_still_feels_like_home_after_all">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/20 06:24

# 为什么这么多年过去,Ruby 依然让我感到亲切 来源:https://caio.ca/blog/why-ruby-still-feels-like-home 我已经用 Ruby 写了十五年以上。最早开始的时候,Rails 3 和 3.1 还是主流,大家还在摸索怎么用 Capistrano 部署。那时我选择它,主要是因为写起来很舒服。这么多年过去,在不同语言的工作和副项目之间切换后,我总会回到 Ruby。它不是最快或最潮的语言,但当我想真正享受编程时,它依然是我的首选。 语言中有一些很少在常规介绍中提到的部分,但它们每天都在默默地消除摩擦。 比如 Ruby 处理方法可见性和 Refinement 的方式。它不只是 private 和 public。你可以用 Refinement 以非常封闭的方式打开一个类,只影响单个文件或块内的代码。当你想要一点语法糖而又不想污染整个命名空间时,这非常完美。在较大的代码库中,我经常用它来写测试辅助方法或内部 DSL。 `` module QuotingRefinement refine String do def quote "\"#{self}\"" end end end using QuotingRefinement puts "hello".quote # 只在调用 using 的地方可用 `` 另一个隐藏的宝藏是标准库中通过 Forwardable 或 SimpleDelegator 实现的委托。你可以获得干净的委托,而无需手动编写包装方法或引入额外的 gem。 `` require "forwardable" class UserRepository extend Forwardable def_delegators :@connection, :query, :transaction def initialize(connection) @connection = connection end end `` 或者,如果你已经在 Rails 环境中,Active Support 中还有更符合人体工学的委托方式,但核心版本对于普通脚本来说已经足够轻量。 Ruby 还有 Object#then 和 Kernel#tap,让你可以链式操作,从上到下阅读,无需中间变量。 `` User.new(params) .tap { |u| Log.info("Created #{u.id}") } .then { |u| u.normalize!; u } .save `` 从 Ruby 2.7 开始,块中的编号参数减少了简短回调中的噪音。 `` items.map { _1.price * 1.1 } `` 还有最近引入的 Fiber 调度器,让你可以编写看似顺序执行,但实际上插入事件循环后就能并发的代码。 `` Fiber.schedule do # 与其他 Fiber 协作 end `` 然后是围绕语言的一些工具,它们很少在 Ruby 圈子之外被提及。Shopify 的 Ruby LSP 提供了优秀的编辑器集成,设置简单,与 Steep 或 RBS 配合使用可实现渐进式类型检查。RuboCop 在不引入其他生态系统中那种繁文缛节的情况下保持代码风格一致。 Ruby LSP (https://github.com/Shopify/ruby-lsp) Steep (https://github.com/soutaro/steep) RuboCop (https://github.com/rubocop/rubocop) 标准库是另一个无声的优势。你不需要为常见任务引入六七个小型 gem,因为你需要的东西通常已经在那里了。需要队列?Thread::Queue 已经稳定运行多年。想要流式解析 JSON?json 和 csv 库无需额外仪式就能处理大多数实际场景。 在性能方面,Ruby 3.x 引入了 YJIT,现在 4.x 系列又有了 ZJIT。ZJIT 是一种更激进的 JIT,建立在相同的基础上,但能更有效地编译热点路径。早期数据显示它在 CPU 密集型任务上缩小了与曾经让 Ruby 望尘莫及的语言之间的差距。 我在同一台机器上用几种语言对一个递归斐波那契实现运行了一个简单的基准测试。在这个特定案例中,开启 ZJIT 的 Ruby 只比 Go 差 2-3 倍,且落后优化后的 Rust 不远,而使用 PyPy 的 Python 仍然落后。当然,微基准测试有局限性,但趋势是真实的。一旦 JIT 有时间优化,实际应用在热代码路径上能获得更大的收益。 CRuby ZJIT (https://github.com/ruby/ruby)(ZJIT 的开发在主仓库中) 与 Rust 相比,Ruby 在业务逻辑的迭代速度上胜出。在 Rust 中,你常常会在运行时显而易见的事情上与借用检查器作斗争。Go 天生提供了优秀的并发原语,但直到最近才有的泛型以及略显僵硬的错误处理,让简单的脚本可能比实际需要的更重。Python 是最接近的远亲,但它在相同的高级概念上,尤其是类和装饰器方面,仍然需要更多的样板代码。 在将代码输入给模型时,词元效率也是真实存在的。Ruby 的语法密度高,这是好事。你可以用 do/end 或花括号表示块,而无需额外关键词。方法调用在可读性允许时很少需要括号。哈希和关键字参数是一等公民且紧凑。所有这些意味着,与迫使你添加更多结构噪音的语言相比,你可以在相同的上下文窗口中向模型展示更多实际逻辑。 一个典型的 Ruby 方法看起来是这样的,既整洁又富有表现力: `` def fetch_dashboard(user_id, include_drafts: false, limit: 20) User .where(id: user_id) .includes(:posts, :profile) .then { |scope| include_drafts ? scope : scope.published } .limit(limit) .first .tap { |u| Log.timing("dashboard.load", user_id) } end `` 同样的思路在其他几种语言中会扩展成更多行,仅仅为了表达默认值、返回类型提示和结构。当你把示例代码粘贴到提示词中时,这些额外的词元很快就会累加。 Ruby 在那些保持可读性的小元编程工具中也表现出色。define_method、class_eval 以及拦截缺失方法的能力,让你无需代码生成步骤就能构建富有表现力的 API。像 dry-rb 或 aasm 这样的库巧妙地使用这些特性,为你提供干净的状态机和验证层。 dry-rb (https://dry-rb.org/) aasm (https://github.com/aasm/aasm) 围绕工具的社区已经成熟了很多。Byebug 和 pry 仍然感觉比我用过的许多其他调试器更流畅。对于后台任务,solid_queue 和 good_job 足够简单,你可以在一个下午理解整个实现。 Solid Queue (https://github.com/rails/solid_queue) Good Job (https://github.com/bensheldon/good_job) 就连部署也变好了。Kamal 取代了很多人以前用的 Capistrano 一套,感觉它是由真正运营小团队的人编写的。 Kamal (https://github.com/basecamp/kamal) 我不是说 Ruby 在每个任务上都胜过其他语言。有些领域 Rust 或 Go 更合适。但对于 Web 应用、后台处理和内部工具这个广阔的中段领域,Ruby 持续提供了开发者幸福感,而无需持续的仪式或上下文切换。 十多年后,这些小小的便利和语言的整体感觉加起来,仍然是我最先使用它的理由。更新的 JIT 工作和稳定的语言改进只强化了这个选择。Ruby 并没有停滞不前。它只是从来不需要大声喧哗。

相似文章

从Rust到Ruby

Hacker News Top

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

Rubish: 一个纯 Ruby 编写的 Unix 外壳

Hacker News Top

Rubish 是一个纯 Ruby 编写的 Unix 外壳,旨在实现与 bash 的完全兼容,同时深度整合 Ruby 的特性,如块、迭代器和方法链。

引用 Mitchell Hashimoto

Simon Willison's Blog

Mitchell Hashimoto 评论编程语言日益增强的可替代性,以 Bun 从 Zig 重写为 Rust 为例,表明语言已不再是锁定效应的来源。

Bun 的 Rust 重写已合并

Lobsters Hottest

Bun,JavaScript 运行时和包管理器,已合并其核心从 Zig 到 Rust 的重写,可能提升性能和可维护性。