---
title: 'Variables and Constants'
source: 'https://academia.sh/en/courses/ruby-fundamentals/variables-and-constants'
course: 'Ruby Fundamentals'
language: en
updated: '2026-10-21T05:03:43+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Tells local variables, instance variables, global variables, and constants apart by their scope sigils'
    - 'Measures assignment with defined? and shows that it is syntax rather than a message'
    - 'Separates compound assignment (+=, ||=) as assignment, and the addition inside it as a message'
---

# Variables and Constants

In Ruby, assignment is syntax rather than a message; local variables, instance variables, global variables, and constants are told apart by the sigil on the name; the limits measured with defined?.

The previous lesson established that every value is an object belonging to a class and that the receiver's class answers the message. One question was left open: how is a name bound to one of these objects? When `x = 5` is written, is a message going to `x`, or to `5`, or is there no message at all? The concept of a variable itself was explained in the Programming Fundamentals course (M01/K02); it is not repeated here. This lesson measures only the Ruby-specific question: when a name is bound to an object, who is at work?

In Ruby, the mark at the start of a name determines what the name is. We call these marks sigils: a lowercase name with no mark is a local variable, a name starting with `@` is an instance variable, a name starting with `$` is a global variable, and a name starting with an uppercase letter is a constant. There is also a class variable that starts with `@@`; it is returned to in the Scope Limits and Parallel Assignment section. The file below queries the four marks, an undefined constant, and an assignment one by one with `defined?`.

## The Name's Sigil Determines the Kind

`defined?` is an expression that says what an expression is without running it. For an undefined name it returns `nil`; for a defined one, a string saying what it is. This is the measuring instrument we will use in the lesson.

```ruby
# scope_sigils.rb — the sigil on a name determines the kind of variable
x = 5
@a = 1
$g = 2

puts defined?(x)
puts defined?(@a)
puts defined?($g)
puts defined?(String)
p defined?(Missing)
puts defined?(result = 5)
```

```
local-variable
instance-variable
global-variable
constant
nil
assignment
```

Six queries, six different answers: `x` is a local variable, `@a` an instance variable, `$g` a global variable, `String` a constant. For the undefined constant `Missing`, `nil` was returned. The last line is the real subject: the expression `result = 5` was reported as `"assignment"`. Neither `"method"` nor `"expression"`; assignment has a name of its own.

**LB7.** In this lesson, "assignment" is the spelling that binds a name to an object with `=`. `defined?` only reads the kind of the expression and does not actually run the assignment; for this reason the name `result` above never came into being. It is known that reassigning to a constant name prints a warning, but because that warning carries a file path it is not printed or measured here; it is mentioned only in prose.

## A Constant Binds the Name and Does Not Freeze the Object

A name starting with an uppercase letter is a constant. Whether a constant is defined can be read with `Object.const_get`. But what does a constant make constant: the object the name is bound to, or the binding of the name? The file below binds a number and an array to one constant each, and then adds an element to the array.

```ruby
# constant_array.rb — a constant binds the name, it does not freeze the object
LIMIT = 10
LIST = [1]

puts Object.const_get(:LIMIT)
puts defined?(LIMIT)
LIST << 2
p LIST
puts LIST.frozen?
puts LIMIT.frozen?
```

```
10
constant
[1, 2]
false
true
```

Although the array was bound to the constant `LIST`, it changed: the constant marks with a warning that the name is being bound to a different object, but it does not lock the contents of the object. The array is not frozen (`false`). Numbers, on the other hand, are already immutable objects, and `frozen?` returns `true` for them. Someone who really wants to make a constant immutable freezes the object separately; freezing is measured on strings in the Symbols and Strings lesson of this course.

## Each Kind Is Kept in Its Own Place

The sigils are not only a convenience of spelling; each kind is stored in a different place. Local variables belong to the scope of the running code itself, instance variables are tied to an object, and global variables are visible across the whole program. Ruby offers methods that list these three. The file below defines the three kinds at the top level and looks each one up in its own list.

```ruby
# variable_lists.rb — each kind is kept in its own list
x = 5
@a = 1
$g = 2

p local_variables
p instance_variables
puts global_variables.include?(:$g)
puts self.inspect
puts self.class
```

```
[:x]
[:@a]
true
main
Object
```

`x` was found in the list of local variables and `@a` in the list of instance variables; `$g` is in the list of global variables. The answer to the question of whose instance variable it is, is `self`: at the top level, `self` is the main object of the file, it prints as `main`, and its class is `Object`. So the `@a` written at the top level is bound to an instance of `Object`; an instance variable is always a variable of an object.

## Assignment Is Not a Message

The rule of the previous lesson was: `5 + 3` is a message, and its responder is the class. The same question can be asked of assignment: if `x = 5` is a message, there must be an owner that answers it. The file below puts three things side by side: plain assignment, a method call that returns a value, and the return value of an assignment.

```ruby
# assignment_value.rb — an assignment expression carries a value
puts defined?(a = 1)
puts defined?(puts)
puts defined?(1 + 2)

value = (b = 7)
puts value
puts b
```

```
assignment
method
method
7
7
```

Three expressions gave three different kinds: the assignment `"assignment"`, the `puts` call `"method"`, the addition `"method"`. So `1 + 2` and `puts` are in the same category, and `a = 1` is separate. Even so, an assignment carries a value: the value of the expression `(b = 7)` is `7`, and `b` was bound as a name. These two observations together say that an assignment is an expression but not a method call.

## Scope Limits and Parallel Assignment

This section measures where names are visible. `defined?` reports only the kind of an expression without running it; here it is used as a scope probe. The axis is syntax: whether a name is visible or not is determined by the scope rules of the language, not by a method defined by a class. Three questions are asked: does a method opened with `def` see the outer local variable, does a block see it, and what kind is a name marked with `@@` opened in a class body?

```ruby
# scope_measurement.rb — how a method and a block see an outer local
outer = 10
def method_inside
  defined?(outer).inspect
end
puts method_inside
block_result = [1].map { defined?(outer) }
puts block_result.first
class Record
  @@count = 0
  def self.counter_kind
    defined?(@@count)
  end
end
puts Record.counter_kind
```

```
nil
local-variable
class variable
```

Three measurements gave three different results. When the outer local variable `outer` was asked about from inside a method opened with `def`, `defined?(outer).inspect` printed `nil`: a method body starts a new scope, and the outer locals do not pass inside. When the same name was asked about from inside a block, `"local-variable"` was returned: a block sees the locals of the place where it was defined. For `@@count` the result was `"class variable"`; this kind, opened in a class body, is a separate category from the `@` and `$` marks. What these three results have in common is that no message answers any of them: the method not seeing outside, the block seeing, and the kind of the `@@` mark all come from the spelling rules of the language. Where a name is defined matters as much as where it is asked: the same name `outer` gave two different answers when asked from two different places.

```ruby
# parallel_assignment.rb — a, b = b, a is an assignment
a = 1
b = 2
a, b = b, a
puts a
puts b
puts defined?(a = 3)
```

```
2
1
assignment
```

The parallel assignment `a, b = b, a` swapped the values of the two names: `a` is now `2` and `b` is `1`. A third temporary variable was not needed, because the right side was evaluated before the names on the left were rebound. `defined?(a = 3)` reported `"assignment"`, which agrees with the finding of the previous section: assignment is the spelling of the language, not a message. `defined?` was asked here of a single assignment, not of the parallel assignment itself; there is also no trace in the output showing that the swap was done with a method call. Swapping two variables is a one-line operation in Ruby; this shows once more that names are labels pointing at objects, and the value is the object behind the label.

**LB8.** The class variable (`@@`) was probed only with `defined?`. How a class variable is shared under inheritance is not the subject of this course and was not measured.

## Assignment Chaining and Destructuring

That assignment is the spelling of the language was measured. This section probes three additional forms of assignment: chained assignment, destructuring into multiple targets, and a missing right side. Each asks the same question: do the names point to the object, or do they copy the value? The axis is syntax; only on one line the owner of the `<<` message is also printed, because after a chained assignment what changes the object is a message.

```ruby
# assignment_chain.rb — chained assignment and destructuring
x = y = [1]
puts x.equal?(y)
y << 2
puts x.inspect
puts x.method(:<<).owner
head, *rest = 10, 20, 30
puts head
puts rest.inspect
first, = [7, 8]
puts first
p1, p2 = 1
puts p2.inspect
puts defined?(Math)
puts defined?(puts)
puts defined?(String.new)
```

```
true
[1, 2]
Array
10
[20, 30]
7
nil
constant
method
method
```

The spelling `x = y = [1]` bound both names to the same object: `x.equal?(y)` gave `true`. This is why, when the array changed through `y << 2`, `x` also showed `[1, 2]`. What made the change here is not the assignment but the `<<` message, and its owner turned out to be `Array`. So the assignment did not copy the object; it only bound a name, and when a message that changes the object arrived, all the names bound to the same object saw the change. This is a test of the intuition built in the earlier sections: a name is a label, and the value is the object behind the label. Assigning a label to another label does not duplicate the object.

The destructuring forms are also the spelling of the language. `head, *rest = 10, 20, 30` gave the first value to `head` and the remaining ones to an array: `head` became `10` and `rest` became `[20, 30]`. The trailing comma in the spelling `first, = [7, 8]` marked that only the first of the targets was taken, and `first` became `7`. When the right side is short, no error was raised: on the line `p1, p2 = 1`, `p2` became `nil`. The last three lines are the answer of the `defined?` probe to different kinds of expression: `constant` for `Math`, and `method` for `puts` and for `String.new`. That the constant is classified as `constant` and the calls as `method` shows that the `defined?` probe looks at what the expression looks like.

**LB9.** That the result of `defined?(Math)` is `constant` was measured in this version. In other versions a different word may be returned for the same constant; the word written in the lesson is the output of this version, not a promise of the language.

## The Definition Query and the Top-Level Object

`defined?` looks not only at local variables but at every kind of name. This section tries the probe on a global variable, an instance variable, and a few expressions, and asks what the top-level `self` object is. The axis is syntax: because `defined?` is an operator of the language rather than a method, the result concerns what the expression looks like.

```ruby
# definition_query.rb — the defined? answer for each kind of name
d1 = defined?($global)
puts d1.inspect
$global = 1
puts defined?($global)
d2 = defined?(@instance)
puts d2.inspect
@instance = 2
puts defined?(@instance)
puts defined?(self)
puts defined?(nil)
puts defined?(String)
puts defined?(3 + 4)
puts self.class
puts self.inspect
```

```
nil
global-variable
nil
instance-variable
self
nil
constant
method
Object
main
```

The global variable `$global` gave `nil` before it was assigned and `"global-variable"` after. The instance variable `@instance` followed the same order: first `nil`, then `"instance-variable"`. So being defined is a consequence not of the name but of the assignment: a name counts as defined only once a value has been bound to it. `defined?(self)` gave `"self"`, `defined?(nil)` gave `"nil"`, and `defined?(String)` turned out to be `"constant"`. `defined?(3 + 4)` reported `"method"`: what concerns the probe is that the outermost operation of the expression is a message.

The last two lines asked about the object this whole file runs inside. `self.class` gave `Object` and `self.inspect` gave `main`. So every expression written without a receiver in a `.rb` file runs inside an object whose class is `Object` and whose name is printed as `main`. The `@instance` variable assigned at the top level is bound to this object too. The implicit receiver of a receiverless call, for example the `puts` that was probed with `defined?` in the previous section, is this `main` object as well. The syntactic scope determines where names are asked; `self` determines in which object the question is answered. The probes of this section arrive at the same point: `defined?` says whether a name is defined and what kind it is, but does not say its value. An undefined name gives `nil`, and a defined name reports its own kind. These two answers are enough to probe a name before using it. Even though the answer `method` came for `3 + 4`, the one giving the answer is still the `defined?` operator: the expression was not run, only its form was looked at.

## The Bounding Measurement

The responder of this lesson is syntax: the name-binding behavior of the `=` mark comes from the spelling of the language itself, not from a method defined by a class. Here the slip happens with `+=`. When `y += 1` is written, because there is an addition inside it, one may think it is a message. The measurement asks: are `y += 1`, `y ||= 3`, and `y + 1` in the same category?

```ruby
# compound_assignment.rb — += is an assignment, but the addition inside it is a message
y = 1

puts defined?(y += 1)
puts defined?(y ||= 3)
puts defined?(y + 1)
puts y
puts y.method(:+).owner
```

```
assignment
assignment
method
1
Integer
```

`defined?(y += 1)` and `defined?(y ||= 3)` both gave `"assignment"`, and `y + 1` gave `"method"`. Then `puts y` still printed `1`: `defined?` did not run the assignment. The responder of the addition is `Integer`, as in the previous lesson. Two jobs are done on the same line: binding the name is the job of the language, that is, syntax; adding two numbers is the job of the class. Only the first is counted as the responder of this lesson, because the lesson is built on name binding.

The common result of the three measurements is this: when a name is written in Ruby, first the sigil of the name is looked at, then the place the sigil points to. The same `x = 5`, once `@` or `$` is put in front, is written to another place, but in all three it is the language itself that runs the `=` mark. For this reason, in later lessons there will be no need to measure assignment separately; when we see `=` in an expression, we do not look for a message.

## Summary

- The mark at the start of a name determines the kind of the name: a lowercase name with no mark is a local variable, `@` an instance variable, `$` a global variable, and an uppercase name a constant.
- `defined?` reports the kind of an expression without running it: `nil` for an undefined constant, `"assignment"` for an assignment.
- A constant marks the binding of a name but does not freeze the object; an array bound to a constant can change.
- Assignment is not a method call: `defined?` gives `"assignment"` for `a = 1` and `"method"` for `1 + 2`.
- The responder of this lesson is syntax: `y += 1` and `y ||= 3` are assignments, and the addition inside is answered by `Integer`.
- `defined?` measures scope too: a method body does not see the outer local (`nil`), a block does (`"local-variable"`); `a, b = b, a` swaps the places, and `x = y = [1]` binds two names to the same object.

## Next Step

Name binding was measured as the work of the language. There are other constructs that work with the language's own spelling: `if`, `while`, and `case`. Of these, who chooses the branches of `case`; is a `when` line sending a message to an object? The next lesson, Control Flow, measures this question.
