In Ruby, what do `puts nil`, `puts []`, `puts [1, [2, nil]]`, `print nil` and `p nil` each write to standard output?
answer
- nil.to_s is empty
- empty text still gets a newline
- empty array: zero iterations
- puts recurses into nested arrays
- p shows nil literally
basics
~20 sputs nil writes an empty line, puts [] writes nothing, puts [1, [2, nil]] writes 1, 2 and an empty line, print nil writes nothing, and p nil writes the text nil plus a newline.
solid answer
~40 s`puts` converts a non-array argument with `to_s`; `nil.to_s` is `""`, and an empty string still gets a newline, so `puts nil` writes a blank line. An array is walked element by element, recursing into nested arrays, so `puts [1, [2, nil]]` writes `1`, `2` and a blank line - and `puts []` iterates zero times and writes nothing at all. `print nil` writes `nil.to_s`, the empty string, with no newline, so nothing appears. `p nil` writes `nil.inspect`, which is the three letters `nil`, followed by a newline, and returns `nil`. The difference matters when a parser yields `nil`: `puts` shows a blank line you can miss, while `p` names the value.
code
ruby · 14 linesputs nil # (blank line)
puts [] # (nothing)
puts [1, [2, nil]] # 1
# 2
# (blank line)
print nil # (nothing)
p nil # nil
print [1, nil, "x"], "\n" # [1, nil, "x"]
loop_list = [1]
loop_list << loop_list
puts loop_list # 1
# [...]go deeper
Recall that nil.to_s is empty while nil.inspect is the word nil, and that puts writes a newline even for empty text but writes nothing for an empty array.
Explain the puts algorithm: strings as-is, arrays recursed element by element, everything else through to_s, and a newline only when the text does not already end with one.
Recognise when blank or missing output hides nil or empty results in a log or a captured-output test, and switch to p or inspect so the value is named.
Push teams toward output that names values unambiguously in diagnostics, so an empty collection, a nil and an empty string can never be confused during an incident.
This question checks whether you know the exact rules `Kernel#puts`, `Kernel#print` and `Kernel#p` follow for **nil**, **empty values** and **arrays** - the inputs that make debugging output misleading. Each rule falls out of two facts: which conversion the method uses, and how `puts` treats arrays. ## The conversions involved - **`nil.to_s`** returns the empty string `""`. - **`nil.inspect`** returns the string `nil`. - **`puts`** and **`print`** use `to_s` for anything that is not already a string; `p` uses `inspect`. - **Arrays** get special treatment in `puts` only. `print` and `p` convert the whole array to one string, and `Array#to_s` is an alias of `Array#inspect`, so both show brackets. ## How puts decides what to write The C implementation of `IO#puts` (which `Kernel#puts` forwards to on standard output) applies these rules to each argument in turn: 1. If the argument is a **String**, it is the line to write. 2. If it **converts to an array**, each element is passed back through `puts` recursively, and nothing else is written for the array itself. 3. Otherwise the line is the argument's **`to_s`**. 4. If the line is **empty**, a single newline is written. 5. Otherwise the line is written, plus a newline **only if it does not already end with one**. With no arguments at all, `puts` writes one newline. The newline `puts` adds is always the character `"\n"`. It is not the output record separator `$\`, which only `print` appends. So a program that set `$\` (itself a legacy practice) still gets exactly one `"\n"` per line from `puts`, while `print` would add `$\` after its last argument. And because `Kernel#puts` forwards to `$stdout.puts`, the same rules apply to `IO#puts` on a file: the conversion and newline logic lives in the `puts` method itself, not in the destination. ## Walking through the five calls | Call | Rule applied | Output | |---|---|---| | `puts nil` | `nil.to_s` is `""`, empty line rule | one blank line | | `puts []` | array rule, zero elements | nothing | | `puts [1, [2, nil]]` | array rule, recursing | `1`, `2`, then a blank line | | `print nil` | `nil.to_s`, no newline | nothing | | `p nil` | `nil.inspect` plus newline | `nil` | Two of those surprise people. `puts []` produces no output even though `puts` with no arguments produces a newline - the empty array is an argument, and iterating it zero times writes nothing. And `puts [nil]` writes a blank line rather than skipping the element, because each element gets the empty-line rule. ## Related traps - **Strings that already end in a newline.** `puts "line\n"` writes one line, not a line and a blank line. `print "line\n"` writes the string as it is. - **Recursive arrays.** An array that contains itself does not loop forever: `puts` writes `[...]` for the recursive reference. - **`print` with an array** writes the `inspect` form on one line: `print [1, nil, "x"]` writes `[1, nil, "x"]`, strings quoted and `nil` spelled out, because `Array#to_s` is `inspect`. - **Return values.** All three calls on `nil` return `nil`, but for different reasons: `puts` and `print` always return `nil`, while `p` returns its argument, which here happens to be `nil`. ## Why interviewers ask it A train-timetable parser that maps rows to departure times might return `nil` for a row it could not parse. Logging those values with `puts` produces blank lines that are easy to overlook in a long listing, and an empty result array produces no output at all - so "nothing printed" can mean either "no rows" or "every row was nil". Using `p` settles it immediately: `p []` prints `[]`, `p [nil, nil]` prints `[nil, nil]`, and `p nil` prints `nil`. The practical rule is short: 1. Use `puts` when the reader is a person and blank lines are harmless. 2. Use `p` whenever the difference between `nil`, `""` and `[]` matters, because only `inspect` spells all three out. 3. Remember that `puts` flattens nested arrays, so it hides structure; `p` and `pp` keep the brackets that show it. Knowing these rules also explains test failures around captured output: an assertion expecting `"\n"` from `puts []` will fail, because the method wrote nothing.
- Why does `print [1, nil, "x"]` show brackets and quotes when `puts` of the same array does not?`print` converts its argument with `to_s`, and `Array#to_s` is an alias of `Array#inspect`, so the whole array becomes one string like `[1, nil, "x"]`. `puts` special-cases arrays: it writes each element separately with that element's own `to_s`, so the brackets and quotes never appear.
- In Ruby, what does `puts "done\n"` write, and why?It writes `done` and a single newline. `puts` only appends a newline when the text does not already end with one, so a string that already ends in a newline is written once, without an extra blank line.
saying these in an interview costs you the question
- puts nil prints the word nil, the same as p nil.
- puts [] prints an empty line, like puts with no arguments.
- print on an array writes each element on its own line.
- puts always adds a newline, so a string ending in a newline gives a blank line.
- puts skips nil elements inside an array.