Most languages don't have a concept of a range of numbers, you simply loop over the numbers you need with a for loop or similar. Ruby, on the other hand, provides ranges of numbers as first class objects.

What are the types of things you can do with an object representing a range of numbers? First and foremost you can iterate over them. Think of that like a C-style "for" loop except you explicitly define the range of numbers you want to iterate over. Ranges can also be used to take slices of strings, generate successive strings, etc. They're not the most important of the Ruby objects, but they do have their uses.

### Range Literals

Ranges can be constructed with the "double dot" syntax. To make a range object representing all the integers from 0 to 10, you could say **0..10**. Note that the smaller number must be on the left. This is the most common form of the **Range** literal, and it produces and object of the type **Range**, which is an Enumerable type.

The above syntax creates *inclusive* ranges. Both the first and last object of the range are considered to be part of the range. The range **0..10** represents all integers from 0 to 10 including 0 and 10. You can also generate *exclusive* ranges by using the "triple dot" syntax. The range **0...10** represents the integers from 0 to 10 *not including* 10. This is a lesser used syntax though, it may lead to confusion.

Like everything else in Ruby, the double dot and triple dot syntax is dynamic. There is no "compile time," so any two values can be used on the left or right hand sides. That said, variables are often used on one of both sides. For example, you can determine how many elements need to be iterated over then iterate over the **0..some_variable** range.

One caveat when using the Range literal syntax is that the double dot and triple dot syntax (or operators) bind very loosely. Namely, the single dot operator (used for method calls) binds more tightly. For this reason, you'll often see the range literals in parentheses. For example, **(0..10).each { … }**.

Finally, the less used **Range.new** method can be used, though it does the same thing and offers no more functionality from the double dot and triple dot syntax. Its arguments are **Range.new(min, max, exclusive)**. Though it may be more clear to some, as the **Range** literal can slightly subtract from readability in some cases.

### Using Ranges

The primary use of ranges is to iterate over as a from of loop, and to determine if an object lies within the range. Since the **Range** class includes the **Enumerable** mixin module, this is done exactly as you'd expect. Iterating is done using the **each** method and testing if an object lies within the range is done with the **include?** method.

```
(0..10).each do|i|
puts i
end
if (0..10).include?(7)
puts "Yes, 7 is between 0 and 10"
end
```

In addition to the typical **each** method for iterating, the **Range** class also provides the **step** method, which will iterate over the range skipping every **n** elements. This can be used to further emulate a typical C-style loop.

```
# Print all elements
(0..10).each do|i|
puts i
end
# Step size of 1 is the same as each
(0..10).step(1) do|i|
puts i
end
# Step size of 2, print only even numbers
# AKA for(i = 0; i <= 10; i += 2)
(0..10).step(2) do|i|
puts i
end
```

### How Ranges Work

Ranges can be used with any objects, not just integers. For example, you can use floating point numbers in the range, but you'll quickly run into a limitation there: you won't be able to iterate over the range. This has to do with how **Ranges** are implemented.

In order to use **(a..b).include?(c)**, c must be greater than or equal to a, and less than or equal to b. As long as a, b and c all respond to the correct operators, they can be used. So you can say things like **('a'..'z').include?('k')**. Since the string **'k'** is greater than and less than the min and max of the range (as defined by the **String#<=>** "spaceship operator" method), the method call will be true.

Similarly, in order to be able to iterate over the range **a..b**, a must be less than b (otherwise the range wouldn't make any sense) and a must respond to the **succ** method. The **succ** method should return a successor to the object. For integers, this means the next highest integer. For strings, this will increment the string from **'a'** to **'b'**, and from **'aaa'** to **'aab'**.

And this brings us back to the original problem. You cannot iterate over the range **10.1..20.1**. Why? Because **10.1** is a **Float** object, and the **Float** class doesn't implement the **succ** method.

Similarly, you cannot iterate over the **10..0** range, but for a different reason. If the first element of the range isn't less than the second element of the range, the range won't function in any way, at least with typical number and string arguments (how your objects implement the comparison operators may be different). Since no number can be both greater than 10 *and* less than 0, no number will ever be included in the range. And since 10 is not less than 0, even though the integer objects implement the **succ** method, you still cannot iterate over them.

### Ranges in Case Statements

One very common place you'll see **Range** objects is in case statements. Since the **Range#===** method is defined as **Range#include?**, if the argument is included in the range it will return true. So, you may see things like this:

```
print "Enter your age: "
age = gets.to_i
case age
when 0...21
puts "You're too young to get in"
when 21..100
puts "OK, you can come in"
end
```