1

I was trying to figure out how the safe navigation operator is implemented but didn't find the source for it, would love to know how it works and how efficient it is.

Holger Just
  • 52,918
  • 14
  • 115
  • 123
dojo1313
  • 11
  • 2
  • 1
    It's more efficient than writing your own conditional if that's what you're asking. – Stefan Aug 13 '18 at 08:51
  • It basically does exactly what your own comparison is going to do: a `nil` check, but done internally, not "in Ruby", which reduces the number of Ruby invocations by combining it into one. `nil` checks are cheap, it is more of just a convenience method, not for "performance". – ForeverZer0 Aug 13 '18 at 09:15

2 Answers2

7

The safe navigation operator was implemented following Feature #11537.

It is part of Ruby's core langiage, i.e. is implemented in the language parser, has an op code in the virtual machine. Thus, there is no single place where the operator is implemented. The first version of it was added in commit a356fe1c but it has seen multiple extensions since then.

As for how efficient it is, the answer is probably: quite. You might want to run your own benchmarks however to confirm whether it suits your requirements.

Holger Just
  • 52,918
  • 14
  • 115
  • 123
2

Before the safe navigation operator was implemented into the core language using C, there used to be a gem with similar functionality called andand. If you want to research how something like this could be implemented using Ruby, then the andand source is a good place to start:

https://github.com/raganwald/andand

require 'andand'

nil.andand.some_method
=> nil
Casper
  • 33,403
  • 4
  • 84
  • 79
  • 4
    That gem's behaviour is not the same as `&.`. For example, `false.andand.foo` returns `false`, but `false&.foo` raises a no method error. The gem's purpose is pretty much different. It is a common beginner's mistake to mix up `nil` and `false` in discussing `&.`. It goes without saying that their implementation are pretty much different. – sawa Aug 13 '18 at 10:08
  • I wouldn't say the purpose is different. The main purpose of both is to get rid of the annoying check of object existence before calling a method on it. But I agree the implementations are different. I posted this more as a curiosity, because it might be fun to learn something new. – Casper Aug 13 '18 at 10:40
  • 1
    I think a better comparison would be with `ActiveSupport`'s `Object#try!`: https://github.com/rails/rails/blob/fc5dd0b85189811062c85520fd70de8389b55aeb/activesupport/lib/active_support/core_ext/object/try.rb#L11-L21 and https://github.com/rails/rails/blob/fc5dd0b85189811062c85520fd70de8389b55aeb/activesupport/lib/active_support/core_ext/object/try.rb#L145-L147 – Tom Lord Aug 13 '18 at 10:58
  • I *think* (?) that there's no behavioural difference between `foo&.bar` and `foo.try!(:bar)`. The only difference is the implementation, and the fact that safe navigation (`&.`) is in the core language, not an extension. – Tom Lord Aug 13 '18 at 11:01
  • 2
    @TomLord `&.` doesn't evaluate method arguments if the receiver is `nil`, whereas `try` does. – Stefan Aug 13 '18 at 12:54
  • @Stefan Nice! TIL. That's also a good justification for not using `try!` in modern ruby. – Tom Lord Aug 13 '18 at 13:58