Ruby中的`try`和`&.`(安全导航操作符)有什么区别?

Adr*_*ien 21 ruby if-statement ruby-on-rails nomethoderror grape-api

这是我的代码:

class Order < Grape::Entity
  expose :id { |order, options| order.id.obfuscate }
  expose :time_left_to_review do |order, options|
    byebug
    order&.time_left_to_review # ERROR
  end
  expose :created_at { |order, options| order.last_transition.created_at }
end

# NoMethodError Exception: undefined method `time_left_to_review' for #<Order:0x007f83b9efc970>
Run Code Online (Sandbox Code Playgroud)

我认为这&.是一个捷径,.try但我想我错了.可能有人指出我正确的方向,我错过了什么?

我觉得这不是红宝石相关的.葡萄可能吗?虽然我不明白它是怎么回事.

Dan*_*sky 40

&.工作#try!,而不是#try.

这里是#try!(来自文档)的描述:

与#try相同,但如果接收不是nil并且未实现尝试的方法,则会引发NoMethodError异常.

所以基本上它可以避免你调用一个方法nil,但是如果一个对象被呈现,它将尝试像往常一样调用它的方法.

引用来自Rails文档,因此重要的是要强调Ruby不提供#try; 它由Rails提供,或者更准确地说是ActiveSupport.该安全导航操作(&.),但是,在红宝石2.3.0提出了语言功能.


tad*_*man 12

这种try方法忽略了很多东西,它只是给它一个镜头,并且如果事情没有成功就把它称为一天.

&有条件的导航选项将阻塞调用nil对象.其他任何内容都被视为有效,并将继续产生全部后果,包括例外情况.


小智 7

除了上面的答案之外,我还添加了一些例子。1

account = Account.new(owner: Object.new)

account.try(:owner).try(:address)
# => nil

account&.owner&.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`

account.try!(:owner).try!(:address)
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,try不检查接收者是否响应给定的方法。而try!和 的&.行为相同。如果接收者响应该address方法,则所有接收者都将返回相同的结果。我更喜欢使用,&.因为它看起来更干净。

有关安全导航操作员 (&.) 的更多信息,我发现此博客非常有用https://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/


Joh*_*eed 5

我来晚了一点,其他答案已经涵盖了它的工作原理,但是我想补充一些其他答案未涉及的内容。

你的问题问的有什么区别try,并&.红宝石。Ruby是这里的关键词。

最大的区别是tryRuby中不存在,它是Rails提供的一种方法。如果您在rails控制台中执行以下操作,则可以看到此信息,也可以自己查看:

[1, 2, 3].try(:join, '-')
#=> "1-2-3" 
Run Code Online (Sandbox Code Playgroud)

但是,如果您在irb控制台中执行相同的操作,则会得到:

[1, 2, 3].try(:join, '-')
NoMethodError: undefined method `try' for [1, 2, 3]:Array
Run Code Online (Sandbox Code Playgroud)

&.是Ruby标准库的一部分,因此可以在任何Ruby项目中使用,而不仅仅是Rails。

  • 在纯 Ruby 中,“foo”或“!foo”,没有“#try” (4认同)
  • 因此,如果您的代码可能在纯 Ruby 中运行,为了安全起见,您应该执行“foo.try(:try, ...)”。 (2认同)
  • 抱歉@mwfearnley,但我不明白你的评论。如果代码是纯 Ruby 语言(即在 Rails 之外或不使用 ActiveSupport),那么如果“foo.try”会引发“NoMethodError”,那么执行“foo.try(:try...)”有什么意义呢? (2认同)
  • @gasc 现在回顾我的评论,我不得不得出结论,这是一个笑话,基于这个建议“显然”不好的事实。当然,如果你几乎可以肯定 `foo.try(:try.. .)` 将会失败,那么你应该执行 `foo.try(:try, :try...)` 以更加安全。 (2认同)