In Ruby, why does "Qty: " + 6 raise TypeError, and what are the idiomatic ways to build that string?
answer
- wants a real String
- Integer has to_s but not to_str
- no implicit conversion of Integer into String
- reverse order asks Integer to coerce
- two numeric strings concatenate
basics
~20 sString#+ accepts only a String or an object with to_str, and Integer has no to_str, so Ruby raises TypeError: no implicit conversion of Integer into String. Convert explicitly with 6.to_s, or build the string with interpolation.
solid answer
~40 sRuby does not convert types behind your back in most operators. `String#+` insists on a String: it calls the implicit converter `to_str` on its argument, and `Integer` defines only the explicit `to_s`, so `"Qty: " + 6` raises `TypeError` with `no implicit conversion of Integer into String`. The fixes all convert explicitly: `"Qty: " + qty.to_s`, `"Qty: #{qty}"`, whose interpolation calls `to_s`, or `format("Qty: %d", qty)`. The reverse order fails differently: `6 + "3"` raises `TypeError` with `String can't be coerced into Integer`, because `Integer#+` asks its argument to coerce itself. To add, parse first: `6 + Integer("3")`. JavaScript would silently concatenate here; Ruby, like Python, refuses.
code
ruby · 10 linesqty = 6
"Qty: " + qty # TypeError: no implicit conversion of Integer into String
"Qty: " + qty.to_s # => "Qty: 6"
"Qty: #{qty}" # => "Qty: 6"
format("Qty: %d", qty) # => "Qty: 6"
qty + "3" # TypeError: String can't be coerced into Integer
qty + Integer("3") # => 9
"6" + "3" # => "63", two CSV cells concatenated, not addedgo deeper
Recall that + does not mix strings and numbers, quote the no implicit conversion message, and show to_s and interpolation as fixes.
Explain that the left operand's method decides the rule: String#+ needs to_str, Integer#+ needs coerce, which is why the two messages differ.
Point to the production trap of numeric strings from files or params concatenating silently, and parse at the boundary before doing arithmetic.
Defend strong typing in core operators as a design choice that surfaces mistakes early, and set conventions for where input text becomes numbers.
## Why + refuses In Ruby, `+` is an ordinary method call on the left operand: `"Qty: " + 6` is `"Qty: ".+(6)`. `String#+` concatenates two strings, so it must turn its argument into a String first. It does not call just any conversion method. It calls **`to_str`**, the *implicit* conversion, which a class defines only when its objects really are strings in all but name. - `String` defines `to_str`, returning itself. - `Integer`, `Float`, `Symbol`, `Array` and `nil` do **not** define `to_str`. - They all define `to_s`, the *explicit* conversion you call yourself when you want a textual representation. So `String#+` finds no `to_str` on `6` and raises `TypeError` with the message `no implicit conversion of Integer into String`. The message names the missing *implicit* conversion; it is not saying `6` cannot be printed. ## The two error messages Which message you get depends on which operand's method runs: | Expression | Method that runs | Result | |---|---|---| | `"Qty: " + 6` | `String#+` | `TypeError`: `no implicit conversion of Integer into String` | | `6 + "3"` | `Integer#+` | `TypeError`: `String can't be coerced into Integer` | | `6 + nil` | `Integer#+` | `TypeError`: `nil can't be coerced into Integer` | | `"6" + "3"` | `String#+` | `"63"`, concatenation, no error | Numeric operators handle unknown operands through the **coerce** protocol: `Integer#+` asks the other object to `coerce` itself into a compatible number. A `String` does not respond to `coerce`, so the attempt fails with the second message. ## Idiomatic fixes Every fix makes the conversion explicit: 1. **Call `to_s`**: `"Qty: " + qty.to_s`. Clear, but repetitive when there are many pieces. 2. **Interpolate**: `"Qty: #{qty}"`. Interpolation calls `to_s` on the embedded value, so it works for any object and is the most common choice. 3. **Format**: `format("Qty: %d", qty)` when you need padding or number formatting. 4. **Parse the other way** when you want arithmetic: `6 + Integer("3")` is `9`. ## Other operators follow the same rule The same implicit protocol governs other core operators, so the pattern generalises: - `[1] + 2` raises `TypeError` with `no implicit conversion of Integer into Array`: `Array#+` wants `to_ary`. - `"ab" * "2"` raises `TypeError` with `no implicit conversion of String into Integer`: `String#*` wants an integer count. - `"total" + :qty` raises `no implicit conversion of Symbol into String`: `Symbol` has `to_s` but no `to_str`. One operator is a trap because it does **not** raise. `String#<<` accepts an `Integer` and treats it as a **codepoint**, so appending `6` to a label adds the invisible control character U+0006 instead of the digit. Convert with `to_s` before appending a number. ## Strictness is deliberate Ruby is dynamically typed but **strongly** typed: values keep their types, and most core methods refuse to guess. The comparison interviewers raise is JavaScript, where `"Qty: " + 6` produces `"Qty: 6"` and `"6" * 1` produces `6`; Python refuses like Ruby. Ruby's refusal surfaces a mistake at the line that made it rather than as a wrong value later. - An operator that accepts "anything that can become a string" would also accept `nil`, turning missing data into empty text. - Implicit conversion is reserved for objects that *are* the target type, so a successful `+` tells you both operands were strings. ## Worked example: a grocery-order CSV A grocery-order file has columns for item, quantity and extra quantity. Every cell a CSV parser returns is a `String`, or `nil` for a blank cell, never an `Integer`. - `row[1] + row[2]` with cells `"6"` and `"3"` returns `"63"`: two strings concatenate without complaint, which is a silent bug rather than an error. - `"Total: " + (Integer(row[1]) + Integer(row[2]))` raises `TypeError`, because the sum is an `Integer`. - Correct: parse each cell with `Integer(...)`, add the integers, then interpolate: `"Total: #{total}"`. A blank cell adds one more trap: `Integer(nil)` raises `TypeError` with `can't convert nil into Integer`, so decide how blanks are handled before summing. ## Summary - `String#+` needs a String or an object with `to_str`; numbers do not qualify. - `Integer#+` needs a number or an object with `coerce`; strings do not qualify. - Convert explicitly with `to_s`, interpolation or `Integer()`, and remember that two numeric strings concatenate instead of adding.
- In Ruby, why is the error for 6 + "3" worded differently from "Qty: " + 6?A different method runs. `6 + "3"` calls `Integer#+`, which handles non-numeric operands by asking them to `coerce`; `String` has no `coerce`, so it raises `String can't be coerced into Integer`. `"Qty: " + 6` calls `String#+`, which needs `to_str`, hence `no implicit conversion of Integer into String`.
- In Ruby, what does label << 6 do when label is a String?It does not raise. `String#<<` treats an `Integer` argument as a codepoint and appends that character, so `6` adds the invisible control character U+0006 rather than the digit. Append `6.to_s`, or interpolate, when you mean the text "6".
saying these in an interview costs you the question
- String#+ calls to_s on its argument, so any object can be appended
- Ruby converts 6 to "6" automatically in a + expression
- "6" + "3" returns 9 in Ruby
- The TypeError means the integer cannot be printed
- 6 + "3" and "Qty: " + 6 raise the same error message