Course outline

Ruby / Foundations: values, methods, text, and valid objects / Lesson 1

Understand Ruby objects, bindings, and numeric meaning

Read small scripts through object references, dynamic values, integer division, mutable aliases, and shallow freezing.

About 5 min readContent reviewed
On this page

Lesson goals

  • Distinguish reassignment of a local variable from mutation of its referenced object.
  • Predict Ruby arithmetic without importing another language’s rounding or immutability assumptions.

State the language and execution assumptions

Ruby expressions work with objects, and variables hold references rather than declared static element types. A local variable can later refer to an object of another class; that flexibility does not guarantee the next method call is meaningful. This course selects Ruby 3.3 documentation as its language baseline and uses synthetic archive records. It does not assume Rails or an installed gem set. Ruby was unavailable during authoring, so all programs are complete or explicitly illustrative source with runnable:false. Expected output is reasoned from the stated inputs, not an observed interpreter result.

Integer division follows Ruby’s numeric contract

Integer division with / returns an integer and rounds toward negative infinity, so a negative non-divisible case differs from truncation toward zero. fdiv requests floating-point division. Ruby integers can grow beyond a fixed machine-word value as resources permit, but that does not make arbitrary calculations free or all target integrations exact. Float uses approximate arithmetic and cannot represent every decimal fraction. Choose units and rounding before formatting output. A displayed decimal is not a complete accounting model, and converting a result after integer division cannot recover the fraction already lost.

ruby Division and a reassigned local reference
puts 7 / 2
puts(-7 / 2)
puts 7.fdiv(2)
value = 7
value = 'archive'
puts value.class
Expected output
3
-4
3.5
String

Expected lines are 3, -4, 3.5, String. The negative quotient exposes floor division, and the final assignment changes the referenced object. No Ruby interpreter executed this source.

Assignment shares a reference unless a copy is made

Assigning an array or mutable string to another variable normally gives another reference to the same object. A mutation through either reference can therefore be visible through both. Reassignment only changes one binding. dup and clone have specific copy behavior, and an ordinary copy is generally shallow: nested objects can remain shared. Do not infer a deep snapshot from a new outer object. Decide who owns mutation and whether a caller receives a live object or an independent value, especially when a model is reused by previews, exports, or later processing.

Freeze protects one object, not its entire graph

freeze prevents supported mutation of the receiver object but does not recursively freeze referenced elements. The example freezes an outer array while deliberately changing a contained string. String.new makes the mutable string explicit rather than depending on a file’s frozen-string-literal setting. A constant name is also not a deep-immutability mechanism; constants reference objects and have their own reassignment conventions. Use immutable value elements or an explicit deeper ownership policy when a stable snapshot is required. Keep freezing, copying, and validating content separate in both implementation and documentation.

ruby A frozen container with a mutable nested string
inner = String.new('raw')
outer = [inner].freeze
inner << '-checked'
puts outer.first
puts outer.frozen?
Expected output
raw-checked
true

Expected output is raw-checked then true. The array is frozen while its element remains mutable. This is a shallow-freeze demonstration, not a recommended immutable model, and it was not run.

Output and labels do not validate a value

puts writes a textual representation with line-oriented behavior, while p uses inspect for diagnostic representation. Interpolation in a double-quoted string evaluates the enclosed expression; single-quoted strings have different interpolation and escape rules. Symbols are name-like objects and are distinct from strings, which matters for hash keys. Keep machine codes independent from display wording and use explicit units in names. A script printing a plausible label has not verified an identifier, permission, or external record. Start with deterministic local values before introducing input, files, or concurrent work with additional failure states.

Common errors and fixes

A negative quotient differs from expected truncation.

Why it happens
Ruby integer floor division was confused with another language’s rule.
How to fix it
Choose the numeric operation and rounding policy explicitly.
Prevent it next time
Include negative non-divisible examples in arithmetic checks.

Changing one string changes another apparent value.

Why it happens
Both variables reference the same mutable object.
How to fix it
Use an intentional copy or restrict mutation to an owner.
Prevent it next time
Review aliasing before promising independent state.

A frozen array’s nested object still changes.

Why it happens
Shallow freezing was mistaken for recursive immutability.
How to fix it
Choose immutable elements or a documented deeper snapshot policy.
Prevent it next time
Inspect every mutable level that the contract exposes.

Check your understanding

1. Predict -9 / 2 and -9.fdiv(2). Why does converting the first result afterward not produce the second?

Show worked solution

The integer quotient is -5 and the floating result is -4.5. Integer division already applied its floor rule, so later converting -5 to a float yields -5.0 rather than recovering the discarded fraction. Select the arithmetic semantics before presentation, and document the intended treatment of remainders.

2. Does assigning an archive array to a constant make its nested strings independent from a caller?

Show worked solution

No. The constant is another binding to an object, and even freezing the outer array would leave nested mutable strings shared. Establish a copy and ownership contract appropriate to the model. If independent immutable values are required, validate and build those values explicitly instead of relying on naming convention.

Key takeaways

  • Ruby bindings, object mutation, copying, and freezing are different mechanisms.
  • Reasoned output is not an executed Ruby result.

Official references