15

What is the difference between these snippets?

def config
  @config ||= begin
                if config_exists?
                  @config = return some value
                else
                  {}
                end
              end
end
 
def config
  @config ||= method
end

def method
  if config_exists?
    return some value
  else
    {}
  end
end

I'm confused with the "begin ... end" block. Does it make any difference in the output? If not, then what is the use of the begin ... end block here?

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Twinkalkumar Savani
  • 258
  • 1
  • 2
  • 11

4 Answers4

18

First of all, you need to be aware that a defined method inherently includes the functionality of a begin ... end block.

In the context of exception handling, def method_name ... end is functionally equivalent to begin ... end. Both can include rescue statements for example.

The two blocks of code you have shared are actually identical, and there is no benefit in one over the other ... unless your method is needed in more than one place. In that case, you DRY up your code by putting the logic into a single method and calling it from multiple other places.

Jon
  • 10,678
  • 2
  • 36
  • 48
10

In your case, you can even omit the begin ... end block:

@config ||=
  if config_exists?  
    return_some_value
  else
    {}
  end

or, using the ternary if:

@config ||= config_exists? ? return_some_value : {}

Does it make any difference in output?

It could make a difference, because unlike def ... end, an begin ... end block doesn't create a new variable scope.

Here's a contrived example:

def foo
  a = 456  # doesn't affect the other a
end

a = 123
b = foo

p a: a, b: b #=> {:a=>123, :b=>456}

Versus:

a = 123
b = begin
  a = 456  # overwrites a
end

p a: a, b: b #=> {:a=>456, :b=>456}
Stefan
  • 109,145
  • 14
  • 143
  • 218
7

Using ||= begin...end allows you to memoize the result of whatever is run in the begin...end. This is useful for caching the result of resource-intensive computation.

shanecav
  • 342
  • 2
  • 7
  • 2
    1. It would be more accurate to say that `@variable ||= code` lets you cache a resource-intensive computation. 2. `begin ... end`, however, lets you cache the result of an expression that is made up of multiple statements ("multi-line statements". – galva Oct 31 '19 at 10:49
  • I can remove my comments if you want to edit your answer. – galva Oct 31 '19 at 10:51
-1

The only thing that will happen differently is if an exception is raised. For instance, let's say there is a problem in the config_exists? method call. If it raises an exception in the first example your @config var will be set to {}. In the second example if the same thing happens your program will crash.

As a side note, there is no need for the return keyword here. In fact the example should read as follows. This is assuming that I understand the intent.

def config
  @config ||= 
    begin
      if config_exists?    
        some_value
      else
        {}
      end
    rescue
      {}
    end
end

and

def config
  @config ||= method
end

def method
  if config_exists?
    some_value
  else
    {}
  end
end

Both examples are exactly the same, except if an exception is raised @config will still be set to = some_value in the first example.

Also, it should be noted that nothing will happen if @config already has a value. The ||= operators is the same as:

@config = some_value if @config.nil?

Only set the variable to this value if it is currently nil.

Hope this is helpful and that I am understanding your question correctly.

Andrew Griffith
  • 249
  • 2
  • 5
  • That's not true - both blocks of code are identical. – Jon Jan 06 '16 at 08:28
  • Maybe I am missing something here, but the `begin` & `rescue` keywords are for handling exceptions. If either `config_exists?` or `some_value` raise an exception, how would it be handled in the second case? – Andrew Griffith Jan 06 '16 at 08:33
  • You're missing the fact that `def` is functionally equivalent to `begin` in terms of exception handling. Also, since neither of these examples include a `rescue` block - nothing good will happen if an exception is raised. – Jon Jan 06 '16 at 08:34
  • I suppose his example lacked the `rescue`. I just assumed it was intended. Otherwise there is no need for the `begin` and `end`. Maybe that is the point you are making. – Andrew Griffith Jan 06 '16 at 08:35
  • Jon, you are of course correct. I just made an assumption about intent. I guess the fact that it made no sense to me personally to have a `begin` without a `rescue`, put me off track. That is why i included the `rescue` in my example. As a side note, is there any good reason to use a `begin` without a `rescue`? maybe there is a use case I am not aware of? – Andrew Griffith Jan 06 '16 at 08:39
  • 1
    You might use it in a loop `begin ... end while conditional` – Jon Jan 06 '16 at 08:51
  • Good point. I was aware of, but have never used that construct. Actually completely slipped my mind. You get into habits I guess. Maybe I will find a place to use it this week. Thanks Jon. – Andrew Griffith Jan 06 '16 at 08:59
  • @AndrewGriffith `my_var = begin v1 = 6 ; v2 = 7 ; v1 * v2 ; end` to assign 42 to `my_var`. – Aleksei Matiushkin Jan 06 '16 at 09:31