In Ruby, how do puts, print, p and pp differ in what they write to standard output and what they return?
answer
- to_s for two, inspect for two
- which ones add a newline
- puts and print return nil
- p returns its argument, or an array
- pp wraps at the terminal width
basics
~10 sputs and print write each argument's to_s (puts adds newlines, print does not) and return nil; p writes inspect plus a newline and returns its argument; pp is p with line-wrapped pretty-printing.
solid answer
~40 s`puts` converts each argument with `to_s`, writes it followed by a newline unless it already ends in one, writes array elements one per line, and returns `nil`. `print` also uses `to_s` but adds no newline between or after its arguments, and returns `nil`. `p` calls `inspect` instead, so strings appear quoted with escapes such as `\r` or a trailing space visible, and it returns its argument - an array of them when given several, `nil` when given none - which lets you wrap it around an expression while debugging. `pp` behaves like `p`, including the return value, but pretty-prints long nested arrays, hashes and objects across several indented lines. So `puts` is for output meant for people, `p` and `pp` are for looking at values.
code
ruby · 14 linesrow = "IC 2041;08:15 ;Leeds\r"
train, departs, dest = row.split(";")
puts departs # 08:15 (trailing space invisible)
p departs # "08:15 "
p dest # "Leeds\r"
result = puts(train)
p result # nil
time = p(departs.strip) # prints "08:15" and returns it
time # => "08:15"
p train, dest # prints two lines, returns ["IC 2041", "Leeds\r"]
print "a", "b" # ab (no newline)go deeper
Recall the pairs: puts and print use to_s and return nil, p and pp use inspect and return what they print. Know that only puts adds newlines per argument and print adds none.
Explain why p is the debugging tool: inspect exposes quotes, nil, symbols and hidden characters, and the return value lets you wrap p around an expression without changing behaviour.
Treat stray puts calls as bugs in review: they return nil and can change a method's result. Prefer p or pp for temporary inspection and a logger for anything that stays.
Frame printing as an output contract: user-facing text, debugging dumps and structured logs are separate channels, and choosing the wrong one leaks internals or loses data in production.
Ruby's `Kernel` module gives every object four ways to write to standard output: `puts`, `print`, `p` and `pp`. They look interchangeable in a tutorial, but they differ on three axes that interviewers probe: **which conversion** they apply (`to_s` or `inspect`), **which newlines** they add, and **what they return**. ## The four methods at a glance | Method | Conversion | Newline | Arrays | Return value | |---|---|---|---|---| | `puts` | `to_s` | after each argument, unless it already ends in `"\n"` | one element per line, nested arrays flattened | `nil` | | `print` | `to_s` | none | written as one `to_s` string | `nil` | | `p` | `inspect` | after each argument | written as one `inspect` string | the argument, an array of arguments, or `nil` | | `pp` | pretty-printed `inspect` | after each argument; long values wrap | wrapped and indented when too wide | same as `p` | Two definitions help: - **`to_s`** is the plain, human-facing text of an object: `"Leeds".to_s` is `Leeds`, `nil.to_s` is the empty string. - **`inspect`** is the developer-facing representation that shows what the value *is*: `"Leeds".inspect` is `"Leeds"` with the quotes, and `nil.inspect` is `nil`. ## puts: text for people `puts` is the everyday output call. For each argument it writes the string (or the result of `to_s`), then a newline - but only if the text does not already end with one, so `puts "done\n"` does not produce a blank line. Called with no arguments it writes a single newline. Given an array, it writes each element on its own line, recursing into nested arrays. It always returns `nil`, which is the source of a classic bug: a method whose last line is `puts total` returns `nil`, not `total`. ## print: no newline at all `print` also converts with `to_s` but writes its arguments back to back with nothing between or after them. `print "a", "b"` writes `ab` with no line break. Two global separators exist - `$,` between arguments and `$\` after the last one - but both are `nil` by default and assigning `$,` a non-nil value is deprecated, so in modern code you add `"\n"` yourself. It returns `nil`. ## p: inspection for debugging `p` calls `inspect` on each argument and writes the result plus a newline. That makes invisible data visible, which is exactly what you want when a value "looks right but compares wrong": - a trailing space or carriage return shows up as `"08:15 "` or `"Leeds\r"`; - `nil` prints as `nil` instead of an empty line; - a symbol prints as `:leeds`, a string as `"leeds"`, so you can tell them apart; - tabs and other control characters appear as escapes such as `\t`. Its **return value** is the other difference that matters: `p x` returns `x`, `p x, y` returns `[x, y]`, and a bare `p` returns `nil`. Because of that you can wrap `p( ... )` around a sub-expression inside a method chain or at the end of a method without changing what the code returns. On CRuby, `p` also flushes standard output after writing, which helps when debug lines interleave with other output. ## pp: pretty-printing for big structures `pp` returns the same values as `p` and writes the same `inspect`-style text, but it lays out large nested data over several lines with indentation. It wraps at the output's width: the terminal width minus one when writing to a terminal, otherwise the `COLUMNS` environment variable, otherwise 80 minus one. Since Ruby 2.5, `Kernel#pp` is available without `require "pp"`; the first call loads the pp library on demand. For an ordinary object it lists instance variables, each pretty-printed in turn. ## Choosing one in practice 1. Writing output a user or another program will read: `puts` (or `print` when you control the newlines). 2. Checking what a variable really holds while debugging: `p`, because `inspect` shows type, quotes and hidden characters. 3. Dumping a deep hash or a list of objects: `pp`, so it stays readable. 4. Leaving a debugging call inside an expression temporarily: `p` or `pp`, never `puts`, because only they return the value they print. A realistic case: a train-timetable parser splits `"IC 2041;08:15 ;Leeds\r"` on `;`. `puts departs` shows `08:15` and `puts dest` shows `Leeds`, so both look fine, yet lookups fail. `p departs` prints `"08:15 "` and `p dest` prints `"Leeds\r"`, revealing the trailing space and carriage return at once. That is the reason experienced Rubyists reach for `p`, not `puts`, when debugging values.
- Why does a Ruby method whose last line is `puts total` return nil, and how would p change that?A Ruby method returns the value of its last expression, and `puts` always returns `nil`, so the caller receives `nil` instead of `total`. Replacing it with `p total` both prints the value (in `inspect` form) and returns `total`, because `Kernel#p` returns its single argument. The cleaner fix is to print at the call site and keep the method's last expression as `total`.
- Does pp need require "pp" in a Ruby 4.0 script, and where does pretty_inspect come from?No. Since Ruby 2.5 `Kernel#pp` is defined up front and loads the pp library on its first call. `Object#pretty_inspect`, which returns the pretty-printed string instead of writing it, is defined by that same library, so it exists once pp has been loaded - by a first `pp` call or by `require "pp"`.
- In Ruby, what do p and pp return when called with no arguments?Both return `nil` and write nothing. `p` returns `nil` for zero arguments, the object for one and an array for several; `pp` follows the same rule, returning `objs.first` (which is `nil` for an empty list) or the array.
saying these in an interview costs you the question
- puts returns the string it printed, so it can safely end a method.
- p and puts both call to_s; the only difference is the newline.
- print adds a newline after its last argument, just like puts.
- pp always needs require "pp" before you can call it.
- p with several arguments returns only the last one.