Làm gì &. (dấu chấm và dấu chấm) có nghĩa là trong Ruby?


Câu trả lời:


205

Nó được gọi là Toán tử điều hướng an toàn. Được giới thiệu trong Ruby 2.3.0, nó cho phép bạn gọi các phương thức trên các đối tượng mà không cần lo lắng rằng đối tượng có thể là nil(Tránh undefined method for nil:NilClasslỗi), tương tự như tryphương thức trong Rails .

Vì vậy bạn có thể viết

@person&.spouse&.name

thay vì

@person.spouse.name if @person && @person.spouse

Từ Tài liệu :

my_object.my_method

Điều này sẽ gửi tin nhắn my_method đến my_object. Bất kỳ đối tượng nào cũng có thể là người nhận nhưng tùy thuộc vào khả năng hiển thị của phương thức, việc gửi tin nhắn có thể tăng NoMethodError.

Bạn có thể sử dụng &. để chỉ định một người nhận, sau đó my_method không được gọi và kết quả là không khi người nhận không. Trong trường hợp đó, các đối số của my_method không được đánh giá.


13
Trên thực tế, nó hoạt động giống như try!phương thức, không phảitry
Grzegorz

58

Lưu ý: Mặc dù @Santosh đã đưa ra câu trả lời rõ ràng và đầy đủ, tôi muốn thêm một số thông tin cơ bản và thêm một lưu ý quan trọng liên quan đến việc sử dụng nó với các biến không phải là cá thể.


Nó được gọi là " Toán tử điều hướng an toàn " ( còn gọi là "Toán tử xích tùy chọn", "Toán tử không có điều kiện", v.v. ). Matz dường như gọi nó là "nhà điều hành cô đơn". Nó được giới thiệu trong Ruby 2.3 . Nó chỉ gửi một phương thức cho một đối tượng nếu không nil.

Thí dụ:

# Call method `.profile` on `user` only if `user` is not `nil`
@user&.profile

# Equivalent to
unless @user.nil?
  @user.profile
end

"Trường hợp cạnh" với các biến cục bộ:

Xin lưu ý, mã ở trên sử dụng các biến thể hiện. Nếu bạn muốn sử dụng toán tử điều hướng an toàn với các biến cục bộ, bạn sẽ phải kiểm tra xem các biến cục bộ của bạn được xác định trước chưa.

# `user` local variable is not defined previous
user&.profile

# This code would throw the following error:
NameError: undefined local variable or method `user' for main:Object

Để khắc phục sự cố này, hãy kiểm tra xem biến cục bộ của bạn được xác định trước hay đặt thành 0:

# Option 1: Check the variable is defined
if defined?(user)
  user&.profile
end

# Option 2: Define your local variable. Example, set it to nil
user = nil
user&.profile     # Works and does not throw any errors

Phương pháp nền

Rails có tryphương pháp về cơ bản là làm như vậy. Nó sử dụng sendphương thức bên trong để gọi một phương thức. Matz cho rằng nó chậm và đây phải là một tính năng ngôn ngữ tích hợp.

Nhiều ngôn ngữ lập trình khác có tính năng tương tự: Objective C, Swift, Python, Scala, CoffeeScript, v.v. Tuy nhiên, một cú pháp phổ biến là ?.(dấu chấm câu). Nhưng, cú pháp này không thể được Ruby chấp nhận. Vì ?được cho phép trong các tên phương thức và do đó, ?.chuỗi ký hiệu đã là mã Ruby hợp lệ. Ví dụ:

2.even?.class  # => TrueClass

Đó là lý do tại sao cộng đồng Ruby phải đưa ra các cú pháp khác nhau. Đó là một cuộc thảo luận tích cực và các tùy chọn khác nhau được coi là ( .?, ?, &&, vv). Dưới đây là danh sách một số cân nhắc:

u.?profile.?thumbnails
u\profile\thumbnails
u!profile!thumbnails
u ? .profile ? .thumbnails
u && .profile && .thumbnails

# And finally
u&.profile&.thumbnails

Trong khi chọn cú pháp, các nhà phát triển đã xem xét các trường hợp cạnh khác nhau và cuộc thảo luận khá hữu ích để đi qua. Nếu bạn muốn xem qua tất cả các biến thể và sắc thái của nhà điều hành, vui lòng xem thảo luận giới thiệu tính năng này trên trình theo dõi vấn đề chính thức của Ruby.


Những gì là '.?' ? Tôi nghĩ rằng nó đã được quyết định rằng cú pháp này sẽ không thể sử dụng. Tôi chỉ có thể nhận được '&.' làm việc.
Keith Bennett

2
Chính xác, như tôi đã viết .?và các lựa chọn khác đã được xem xét, nhưng &.đã được chọn! Vì vậy, chỉ &.sẽ làm việc.
Uzbekjon

Ồ, xin lỗi, tôi đã không đọc kỹ cho đến cuối cùng. Sẽ không tốt hơn nếu các ví dụ có cú pháp đúng?
Keith Bennett

@KeithBennett Tôi hiểu ý của bạn. Tôi đã có một lỗi đánh máy trong ví dụ đầu tiên của tôi. Bạn đúng, nó phải &.và không .?, xấu của tôi. Cập nhật câu trả lời của tôi. Cảm ơn cho những người đứng đầu lên!
Uzbekjon

@Uzbekjon Vẫn còn một lỗi đánh máy trong ví dụ trường hợp Edge (tùy chọn 1): user.?profilenên user&.profile(không thể tự chỉnh sửa vì đây chỉ là chỉnh sửa 1 ký tự!).
Yanis Vieilly

7

Hãy cảnh giác! Mặc dù toán tử điều hướng an toàn thuận tiện nhưng cũng có thể dễ dàng tự lừa mình thay đổi logic của bạn với nó. Tôi khuyên bạn nên tránh sử dụng nó trong kiểm soát dòng chảy. Thí dụ:

str = nil

puts "Hello" if str.nil? || str.empty?
# The above line is different than the below line
puts "Hello" if str&.empty?

Trong ví dụ đầu tiên, str.nil?trả về truestr.empty?không bao giờ được gọi, khiến putscâu lệnh được thực thi. Tuy nhiên, trong ví dụ thứ hai, str&.empty?trả về nilđó là falsey và putscâu lệnh không bao giờ được thực thi.


Thật thú vị, mặc dù về mặt kỹ thuật trong tuyên bố đầu tiên của bạn str.nil?là đúng (như bạn nói) có nghĩa là str.empty?thực sự sẽ không được gọi (đó là cách thức ||hoạt động của nhà điều hành). Phần còn lại của tuyên bố của bạn là chính xác.
Ollie Bennett

1
Oh bắt tốt. Tôi không chắc làm thế nào tôi bỏ lỡ điều đó. Tôi sẽ sửa bài viết của mình. Cảm ơn bạn!
Thomas Deranek

2
đúng là sử dụng logic sai dễ dàng hơn nhiều, nhưng bạn đang kiểm tra những thứ khác nhau ở đây. dòng thứ hai tương đương với nếu str && str.empty? không phải | | trống. toán tử điều hướng an toàn vẫn hoàn toàn hợp lệ để sử dụng cho điều khiển luồng puts "hello" unless str&.present?(chỉ dành cho phương pháp hiện tại?)
Sampson Crowley

1

nó được sử dụng để kiểm tra con số không, chẳng hạn như trong kotlin và swift Chẳng hạn; với Object -> Swift và Kotlin

model = car?.model

mô hình này có thể là con số không (Swift) hoặc null (Kotlin) nếu chúng ta chưa xác định giá trị mô hình trong lớp xe hơi. chúng tôi sử dụng ký hiệu đó thay vì dấu hỏi trong ruby

model = car&.model

nếu sử dụng car.model mà không có ký hiệu và nếu model không thì hệ thống không thể tiếp tục chạy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.