← Quay lại
27 tháng 12, 2025 6 phút đọc

Có gì mới trong Ruby 4

⭐️Cập nhật 30/12/2025: Mới đây trang web chính thức của Ruby cũng đã đăng release note cho Ruby 4.0. Mình có thêm thông tin về Ruby::Box và ZJIT.

-----------------------------

Bài viết này tham khảo chủ yếu từ tổng hợp của Victor Shepelev (zverok). Ở đây mình lấy ra những thứ mà mình cảm thấy thú vị và có thể dùng khi làm việc, có những thay đổi khác mà mình ít dùng và cũng không rành thì không liệt kê ra, các bạn có thể tham khảo thêm ở link trên. Và cảm ơn zverok đã tổng hợp kèm ví dụ!

Toán tử logic được phép nằm đầu câu

Đây là thay đổi mình thích nhất. Nếu bạn quen với việc viết method chaining xuống dòng với dấu chấm ở đầu, thì hẳn cũng sẽ nghĩ như mình.

Trước đây, Ruby bắt buộc các toán tử logic (||, &&) phải nằm ở cuối dòng nếu muốn ngắt câu điều kiện dài. Điều này khá phiền phức khi chỉnh sửa code hoặc debug. Tin vui là ở phiên bản mới, Ruby đã cho phép đặt chúng ở đầu dòng tiếp theo. Ví dụ:

# TRƯỚC ĐÂY
# Bạn bắt buộc phải để && ở cuối, nhìn khá lộn xộn nếu có comment
if user.active? &&
   user.has_permission? &&
   !user.banned?
  do_something
end

# BÂY GIỜ (Ruby 4.0)
# Code thoáng hơn, giống hệt cách chaining
if user.active?
  && user.has_permission?
  # Có thể thêm comment phía trên điều kiện
  && !user.banned?
  do_something
end

Lợi ích lớn nhất nằm ở trải nghiệm khi DebugRefactor code.

Vô hiệu hóa nhanh (Comment out): Bạn có thể tạm thời tắt một điều kiện bất kỳ bằng cách comment ngay đầu dòng mà không gây lỗi cú pháp cho dòng phía trước.

if user.active?
  # && user.has_permission?  <-- Comment dòng này mà không gây lỗi
  && !user.banned?
  do_something
end

Ở cách viết cũ, nếu comment dòng cuối thì phải nhảy lên dòng trên để xóa dấu && thừa, rất mất công.

Git Diff sạch: Khi thêm hoặc xóa một điều kiện, bạn chỉ thao tác trên đúng một dòng đó. Git sẽ chỉ hiển thị thay đổi ở 1 dòng thay vì 2 dòng (dòng sửa và dòng chứa dấu toán tử cũ).

*nil không còn gọi #to_a

Sẵn nhắc lại dấu * đứng trước một đối tượng trong Ruby có nghĩa là làm phẳng nó (splat) khi truyền vào tham số của một hàm. Ví dụ add(x, y) nhận 2 tham số, nhưng nếu bạn có một biến num là mảng 2 phần tử num = [1, 2] thay vì gọi hàm add(num[0], num[1]) có thể dùng add(*num). Nó biến một mảng thành 2 phần tử riêng lẻ.

Trước đây *nil sẽ gọi nil.to_a nhưng giờ không cần nữa vì nếu nil thì không có giá trị nào nên tại sao phải gọi to_a để tạo một object mới làm tốn tài nguyên và chậm hệ thống. Phiên bản trước, **nil đã sửa để không gọi nil.to_hash nữa để tăng hiệu suất và ở phiên bản này áp dụng cho *nil.

Để kiểm tra xem *nil có gọi to_a tạo mảng mới không ta có thể thử code này

# Mỗi lần gọi nil.to_a sẽ tạo object mới
def nil.to_a = [1, 2, 3]

# Hàm kiểm tra xem *nil có gọi to_a không
def m(*args) = p(args:)

m(*nil)
# Ruby 3.4: in ra args: [1, 2, 3] -- gọi nil.to_a định nghĩa lại [1, 2, 3]
# Ruby 4.0: in ra args: [] -- bỏ qua bước tạo array

Tuỳ chỉnh Object#inspect

Khi inspect một object, tất cả instance variable đều được in ra. Điều này nhiều khi khá dư thừa và lộ ra những thứ bạn có thể không muốn bày ra cho thiên hạ. Phiên bản này giới thiệu #instance_variables_to_inspect nhằm giới hạn các instance variable được in ra khi inspect.

# Trước đây
class Account
  def initialize(username, membership)
    @username = username
    @membership = membership
  end

  def greet
    p "Hello #@username!"
  end
end

account = Account.new('nukun', 'vip')
# => #<Account:0x0000000126190420 @membership="vip", @username="nukun">

# Ruby 4
# thêm vào class
def instance_variables_to_inspect
  [:@username]
end

account = Account.new('nukun', 'vip')
# => #<Account:0x00000001231fb3e0 @username="nukun">

String#strip và các biến thể

Giờ đây có thể thêm tham số cho #strip và các biến thể của nó như #strip!, #rstrip,... để xoá bỏ kí tự cụ thể khỏi đầu, cuối string. Trước đây, mặc định là xoá khoảng trắng.

'2có 2 số ở đầu và cuối chuỗi5'.strip('1-9')
# => "có 2 số ở đầu và cuối chuỗi"

Array#find và rfind

Giờ chúng được triển khai riêng cho Array thay vì dùng các hàm của module Enumerable, nên sẽ nhanh hơn. Ví dụ

[1, 2, 3, 4, 5].rfind { it < 5 }
# nhanh hơn so với
[1, 2, 3, 4, 5].reverse.find { it < 5 }

Sửa hành vi #overlap? cho khoảng không giới hạn

Với khoảng vô tận, hãy tự hình dung xem những mệnh đề sau trả về đúng hay sai

(nil..nil).overlap? (nil..3)
# true hay false?, và ngược lại
(nil..3).overlap? (nil..nil)
# false hay true :v

Có câu trả lời chưa? Với phiên bản cũ, từ vô cùng tới 3 lại không chồng lên khoảng vô cùng, thế mới hay. Giờ đã fix trong Ruby 4. Đáp án cho 2 câu trên đều là true nhé.

Và các thay đổi khác mà các bạn có thể tìm hiểu thêm như:
  • Set thành lớp "core" thay vì là thư viện tiêu chuẩn và được viết lại bằng code C, nên sẽ nhanh hơn trước đấy.
  • Pathname cũng thành core. Giờ không cần phải require 'pathname' mới dùng được như trước đây.
  • Giới thiệu Ruby::Box, khái niệm mới để cô lập môi trường hoạt động trên cùng một process. Nó giúp cô lập test case để bảo vệ các test khác khỏi bị ảnh hưởng của monkey patch, hay 2 môi trường deploy song song trên một process để test,... 
  • Một vài thay đổi về Exception
  • Socket.tcp & TCPSocket.new nhận thêm tham số open_timeout:
  • Cải tiến cho RactorFiber. Hai class phục vụ đa nhiệm nhưng với triết lý khác nhau.
  • ZJIT là trình biên dịch just-in-time mới, được hướng đến là thế hệ tiếp theo của YJIT (của Shopify) nhằm tăng hiệu năng của app Ruby. Hiện vẫn không bằng YJIT và chỉ mới ở mức "thử nghiệm".

Cài ngay Ruby 4 và test thử nào anh em!
Cảm ơn bạn đã đọc bài viết này.
0