Question

Ruby String Concatenation: Using Interpolation, +, and <<

rubystring-concatenation

Question

I am looking for a more elegant way to concatenate strings in Ruby.

I currently have this line:

copy_source = "#{ROOT_DIR}/" << project << "/App.config"

Is there a cleaner or more idiomatic way to write this in Ruby?

Also, what is the difference between << and + when concatenating strings?

Short Answer

By the end of this page, you will understand the main ways to combine strings in Ruby: string interpolation, +, and <<. You will learn which approach is most idiomatic, how mutation works, when each option is appropriate, and how to avoid common mistakes when building file paths or messages.

Concept

In Ruby, string concatenation means combining smaller strings into one larger string. Ruby gives you several ways to do this, and the best choice depends on readability, performance, and whether you want to modify an existing string.

The three most common approaches are:

"Hello, " + name
"Hello, #{name}"
message = "Hello, "
message << name

Why this matters

String building appears everywhere in real programs:

  • generating file paths
  • formatting log messages
  • building URLs
  • producing user-facing text
  • assembling SQL snippets or commands carefully

In Ruby, these methods are not identical:

  • + returns a new string
  • << appends to the existing string and mutates it
  • interpolation (#{...}) is usually the most readable when inserting values into a string literal

For your example, the most idiomatic Ruby version is often:

copy_source = "#{ROOT_DIR}/#{project}/App.config"

This is easier to read because the whole final string is visible in one place.

If you are specifically building file paths, Ruby also provides safer tools such as:

File.join(ROOT_DIR, project, "App.config")

That avoids manually worrying about slashes.

Mental Model

Think of a string like a piece of paper with text on it.

  • + is like taking two papers and making a new combined copy.
  • << is like writing extra text directly onto the same paper.
  • interpolation is like filling in blanks in a prepared sentence template.

For example:

  • "Hello, " + name → make a new paper
  • greeting << name → modify the original paper
  • "Hello, #{name}" → use a template with a placeholder

This difference matters because modifying the original object can affect other variables that point to the same string.

Take Quiz

Syntax and Examples

Core syntax

1. String interpolation

name = "Ava"
message = "Hello, #{name}!"

Interpolation is usually the clearest choice when you are inserting values into a string.

2. Using +

name = "Ava"
message = "Hello, " + name + "!"

This creates a new string each time + is used.

3. Using <<

name = "Ava"
message = "Hello, "
message << name << "!"

This appends to message directly.

Your example

A cleaner version using interpolation:

copy_source = "#{ROOT_DIR}/#{project}/App.config"

A better version for file paths:

copy_source = File.join(ROOT_DIR, project, "App.config")

Why File.join is often better

When you build file paths manually, it is easy to accidentally create:

  • double slashes
  • missing slashes
  • platform-specific issues

Example:

root_dir = "/my/app"
project = "demo"

copy_source = File.join(root_dir, project, "App.config")
# => "/my/app/demo/App.config"

This is clearer and safer than manually inserting / everywhere.

Step by Step Execution

Consider this code:

root_dir = "/projects"
project = "alpha"
copy_source = "#{root_dir}/" << project << "/App.config"

Step by step

  1. Ruby evaluates "#{root_dir}/"

    • root_dir is "/projects"
    • result becomes "/projects/"
  2. Ruby applies << project

    • appends "alpha" to the existing string
    • string becomes "/projects/alpha"
  3. Ruby applies << "/App.config"

    • appends "/App.config"
    • final result is "/projects/alpha/App.config"

So the final value is:

"/projects/alpha/App.config"

Now compare with +:

copy_source = root_dir + "/" + project + "/App.config"

What happens here

  1. root_dir + "/" creates a new string
  2. that new string + project creates another new string
  3. that result + "/App.config" creates yet another new string

The output is the same, but the process is different: + creates new strings, while << modifies one existing string.

Real World Use Cases

Common places string concatenation is used

File paths

File.join("/var", "log", "app.log")

Used when locating config files, uploads, or templates.

Log messages

puts "User #{user_id} logged in at #{Time.now}"

Interpolation is very readable for logs.

URLs

base_url = "https://api.example.com"
endpoint = "users"
url = "#{base_url}/#{endpoint}"

Useful when building API requests.

Report generation

line = "Name: #{user.name}, Email: #{user.email}"

Good for CSV-like output or plain text exports.

Building large strings efficiently

output = ""
items.each do |item|
  output << item.to_s << "\n"
end

Appending with << is common when building output inside loops.

Take Quiz

Real Codebase Usage

In real Ruby projects, developers usually choose the string-building style based on intent.

Common patterns

Prefer interpolation for readability

error_message = "Invalid email: #{email}"

This is usually easier to read than multiple + operations.

Use File.join for paths

config_path = File.join(ROOT_DIR, project, "App.config")

This is the standard approach in codebases because it is less error-prone.

Use << when building strings repeatedly

buffer = ""
records.each do |record|
  buffer << record.name << "," << record.id.to_s << "\n"
end

This avoids creating many temporary strings.

Guard against nil

name = user.name || "Guest"
message = "Hello, #{name}"

Real code often needs validation before combining values.

Use early formatting instead of scattered concatenation

Less clear:

message = "Error: "
message << code.to_s
message << " for user "
message << user_id.to_s

Clearer:

message = "Error: #{code} for user #{user_id}"

In production code, readability usually matters more than cleverness.

Common Mistakes

1. Using << without realizing it mutates the string

a = "Hello"
b = a
b << " world"

puts a
# => "Hello world"

Why this happens:

  • a and b refer to the same string object
  • << changes that object

How to avoid it:

a = "Hello"
b = a.dup
b << " world"

2. Using + with nil

Broken code:

name = nil
message = "Hello, " + name

This raises an error because nil is not a string.

Safer options:

message = "Hello, #{name}"

or

message = "Hello, " + name.to_s

3. Manually building file paths

Less safe:

path = root_dir + "/" + project + "/App.config"

Better:

path = File.join(root_dir, project, "App.config")

4. Mixing interpolation and mutation in a confusing way

path = "#{root_dir}/" << project << "/App.config"

This works, but many developers find a single interpolation expression easier to read:

path = "#{root_dir}/#{project}/App.config"

5. Forgetting that + creates new strings

Inside a loop, this can be less efficient:

result = ""
items.each do |item|
  result = result + item
end

Better when repeatedly appending:

result = ""
items.each do |item|
  result << item
end

Comparisons

ApproachMutates existing string?Returns new string?Best use case
Interpolation ("#{value}")NoYesReadable string templates
+NoYesSimple one-off concatenation
<<YesNoAppending efficiently, especially in loops
File.joinNoYesBuilding file paths

Interpolation vs +

"Hello, #{name}"
"Hello, " + name
  • interpolation is usually easier to read
  • + is fine for simple cases
  • interpolation automatically converts many values using string conversion rules more naturally in templates

+ vs <<

a = "Hi"
b = a + "!"
# a is still "Hi"

c = "Hi"
c << "!"
# c is now "Hi!"

Choose + when you want to keep the original unchanged. Choose << when you intentionally want to append to the same string.

Manual path building vs File.join

"#{ROOT_DIR}/#{project}/App.config"
File.join(ROOT_DIR, project, "App.config")

Use File.join for paths because it expresses intent clearly and avoids separator mistakes.

Take Quiz

Cheat Sheet

# Interpolation
"Hello, #{name}"

# Concatenation with +
"Hello, " + name

# Append with <<
message = "Hello, "
message << name

Quick rules

  • + creates a new string
  • << modifies the existing string
  • interpolation is usually the most readable for templates
  • use File.join for file paths
  • use << when building a string repeatedly in a loop

Good examples

full_name = "#{first} #{last}"
path = File.join(ROOT_DIR, project, "App.config")

Watch out for

"Hello, " + nil      # error

Safer:

"Hello, #{nil}"      # becomes "Hello, "
"Hello, " + nil.to_s

Mutation reminder

a = "x"
b = a
b << "y"
puts a  # => "xy"

FAQ

What is the most idiomatic way to concatenate strings in Ruby?

Usually string interpolation, such as "#{name}", is the most idiomatic when building readable strings. For file paths, prefer File.join.

What is the difference between << and + in Ruby?

+ returns a new string. << appends to the existing string and changes it.

Is << faster than + in Ruby?

Often yes when repeatedly building strings, because it avoids creating as many temporary string objects.

Should I use interpolation for file paths?

It works, but File.join is usually better because it is clearer and safer for path construction.

Can << cause bugs?

Yes. If two variables reference the same string, appending with << changes that shared object.

Does interpolation work with numbers and other values?

Yes. Ruby will convert the value to a string inside #{...}.

Why does "text" + nil fail?

Because + expects another string. nil is not a string unless you convert it with to_s.

Take Quiz

Mini Project

Description

Build a small Ruby script that generates file paths for multiple projects. This demonstrates readable string construction and shows why File.join is often better than manual concatenation when working with file system paths.

Goal

Create a Ruby program that builds config file paths for several project names and prints them.

Requirements

Requirement 1 Requirement 2 Requirement 3

Take Quiz

Keep learning

Related questions

View more