Search code examples
rubywhile-looprubocop

Rubocop rule: Never use 'do' with multi-line 'while


I have the following code

  # colours a random cell with a correct colour
  def colour_random!
    while true do
      col, row = rand(columns), rand(rows)
      cell = self[row,col]
      if cell.empty? then
        cell.should_be_filled? ? cell.colour!(1) : cell.colour!(0)
        break
      end
    end
  end

it's not that important what's doing, although it should pretty obvious. The point is that Rubocop gives me a warning

Never use 'do' with multi-line 'while

Why should I not do that? How should I do it then?


Solution

  • while is a keyword,so you don't need to pass a block. Without do..end it will work fine. The below is fine

      def colour_random!
        while true
          col, row = rand(columns), rand(rows)
          cell = self[row,col]
          if cell.empty? then
            cell.should_be_filled? ? cell.colour!(1) : cell.colour!(0)
            break
          end
        end
      end
    

    while is a keyword, and if you pass a block to it, like do..end, it still works as you asked it to do, by not throwing any error, rather just a warning. But it could be dangerous if you try to pass a Proc or Method object to it, and dynamically try to convert it to a block using & keyword, as we do generally. That means

    # below code will work as expected just throwing an warning.
    x = 2
    while x < 2 do
      #code
    end
    

    But if you try to do by mistake like below

    while &block # booom!! error
    

    The reason is while is a keyword, which don't support any to_proc method to satisfy your need. So it can be dangerous.

    Ruby style guide also suggested that Never use while/until condition do for multi-line while/until

    I think the reason is as Nobuyoshi Nakada said in the mailing list

    loop is a kernel method which takes a block. A block introduces new local variable scope.

      loop do
        a = 1
        break   
      end   
      p a #=> causes NameError
    

    while doesn't.

      while 1
        a = 1
        break   
      end
      p a #=> 1