Sử dụng (&. ) trong Ruby
Bài đăng này đã không được cập nhật trong 7 năm
1.Phương thức Try ()
Chắc hẳn, trong Rails, các bạn sẽ không lại gì phương thức try() này nhỉ. Cơ bản thì try() giúp chúng ta gọi các method của 1 object mà không cần lo lắng về việc object đó có phải là nil hay không và việc gây ra các exception không muốn. Cách sử dụng thì nhìn vào ví dụ dưới đây chúng ta sẽ thấy
user.try(:name)
tương tự
user.nil? ? nil : user.name
2. Safe Navigation Operator (&.)
Safe Navigation Operator được đưa vào từ Ruby 2.3.0 Chúng ta sẽ bắt đầu với ví dụ dưới đây nhé, đây là cách viết an toàn thông thường để đảm bảo không có các exception không mong muốn bắn ra khi object bị nil
if account && account.owner && account.owner.address
...
end
Thật là dài dòng và phiền nhiễu, nhưng với method try() chúng ta có thể rút ngắn như sau
if account.try(:owner).try(:address)
...
end
Có thể thấy cả 2 cách viết trên đều trả về kết quả như nhau
3. Sử dụng (&.)
Chúng ta có thể viết ví dụ trên bằng cách sử dụng (&.) như sau
account&.owner&.address
Có thể thấy cú pháp hơi lạ 1 chút nhưng đoạn code của chúng ta trông sẽ ngắn gọn đi tương đối nhỉ
4. Ví dụ khác
So sánh 3 cách viết ở trên với 1 bài toán khác
account = Account.new(owner: nil) # account without an owner
account.owner.address
# => NoMethodError: undefined method `address' for nil:NilClass
account && account.owner && account.owner.address
# => nil
account.try(:owner).try(:address)
# => nil
account&.owner&.address
# => nil
Nếu tất cả là nil thì kết quả sẽ giống nhau, không có gì đặc biệt cả, vậy nếu owner là false thì sẽ như thế nào?
account = Account.new(owner: false)
account.owner.address
# => NoMethodError: undefined method `address' for false:FalseClass `
account && account.owner && account.owner.address
# => false
account.try(:owner).try(:address)
# => nil
account&.owner&.address
# => undefined method `address' for false:FalseClass`
Có thể thấy cú pháp &. chỉ hoạt động khi object nil còn trường hợp false thì bắn ra exception Vậy chuyện gì nếu owner present nhưng không trả về address
account = Account.new(owner: Object.new)
account.owner.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>
account && account.owner && account.owner.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
account.try(:owner).try(:address)
# => nil
account&.owner&.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
Có thể thấy chỉ có try() trả về nil, còn 2 cách viết kia đều bắn ra exception. Vậy việc sử dung (.&) hơn gì so với try
4. Tại sao nên sử dụng (&.)
(1) &. giúp code ngắn gọn hơn try(...) Nếu cùng 1 kịch bản thì (&.) sẽ giúp code ngắn gọn hơn tương đối kể (2) &. là cú pháp của Ruby Phương thức try() không được định nghĩa trong thư viện Ruby core mà là của thư viện Rails (3) &. giúp debug dễ dàng hơn (&.) sẽ đưa ra exception nếu 1 phương thức thực sự không tồn tại được gọi, trong khi try() luôn luôn trả về nil
>> "s"&.glubsch
NoMethodError: undefined method `glubsch' for "s":String
Sẽ chỉ trả về nil khi object gọi phương đó nil
>> nil&.glubsch
=> nil
Còn try() thì sao
>> "s".try(:glubsch)
=> nil
Vậy tại sao lại debug nhanh hơn nhỉ??? Thử tưởng tượng nhé, giả sử phương thức glubsch là tồn tại. Sau đó, bạn quyết định đổi tên phương thức đó nhưng lại bị sót ở 1 số nơi. Thì với (&.) sẽ bắn ra exception ngay lập tức khi gọi đến dòng lệnh chứa phương thức sai đó. Trong khi đó phương thức try() thì ngược lại, luôn luôn trả về nil. Điều này sẽ khiến chúng ta mất thời gian để debug tìm ra lỗi đó
Tài liệu tham khảo
All rights reserved