while

A while executes its body as long as its condition is truthy.

  1. while some_condition
  2. do_this
  3. end

The condition is first tested and, if truthy, the body is executed. That is, the body might never be executed.

Similar to an if, if a while‘s condition is a variable, the variable is guaranteed to not be nil inside the body. If the condition is an var.is_a?(Type) test, var is guaranteed to be of type Type inside the body. And if the condition is a var.responds_to?(:method), var is guaranteed to be of a type that responds to that method.

The type of a variable after a while depends on the type it had before the while and the type it had before leaving the while‘s body:

  1. a = 1
  2. while some_condition
  3. # a : Int32 | String
  4. a = "hello"
  5. # a : String
  6. a.size
  7. end
  8. # a : Int32 | String

Checking the condition at the end of a loop

If you need to execute the body at least once and then check for a breaking condition, you can do this:

  1. while true
  2. do_something
  3. break if some_condition
  4. end

Or use loop, found in the standard library:

  1. loop do
  2. do_something
  3. break if some_condition
  4. end

As an expression

The value of a while is the value of the break expression that exits the while‘s body:

  1. a = 0
  2. x = while a < 5
  3. a += 1
  4. break "four" if a == 4
  5. break "three" if a == 3
  6. end
  7. x # => "three"

If the while loop ends normally (because its condition became false), the value is nil:

  1. x = while 1 > 2
  2. break 3
  3. end
  4. x # => nil

break expressions with no arguments also return nil:

  1. x = while 2 > 1
  2. break
  3. end
  4. x # => nil

break expressions with multiple arguments are packed into Tuple instances:

  1. x = while 2 > 1
  2. break 3, 4
  3. end
  4. x # => {3, 4}
  5. typeof(x) # => Tuple(Int32, Int32)

The type of a while is the union of the types of all break expressions in the body, plus Nil because the condition can fail:

  1. x = while 1 > 2
  2. if rand < 0.5
  3. break 3
  4. else
  5. break '4'
  6. end
  7. end
  8. typeof(x) # => (Char | Int32 | Nil)

However, if the condition is exactly the true literal, then its effect is excluded from the return value and return type:

  1. x = while true
  2. break 1
  3. end
  4. x # => 1
  5. typeof(x) # => Int32

In particular, a while true expression with no breaks has the NoReturn type, since the loop can never be exited in the same scope:

  1. x = while true
  2. puts "yes"
  3. end
  4. x # unreachable
  5. typeof(x) # => NoReturn