rubyguides

String#include?

str.include?(other_str) -> true | false

String#include? tells you whether one piece of text contains another. It returns true when the substring is found anywhere inside the string, false when it is not. This is one of the most common checks in Ruby — validating input, branching on file types, guarding against keywords.

Signature

str.include?(other_str) -> true | false
ParameterTypeDescription
other_strStringThe substring to search for

include? takes exactly one argument — the substring you want to find. It does not accept a regular expression. If you need regex matching, reach for String#match? instead.

Return Value

Always returns a boolean: true or false. It never returns nil, and it never returns the position where the match was found. For position information, use String#index, which returns an integer or nil.

Basic substring checks

"Hello, World!".include?("Hello")  # => true
"Hello, World!".include?("World")  # => true
"Hello, World!".include?("Ruby")   # => false

The search is literal: characters must match exactly, including case. Ruby walks through the text byte by byte and compares against the needle starting at each position. When a mismatch is found at one position, Ruby advances to the next character in the haystack and tries again. This is a straightforward linear scan; the method does not use any advanced substring-search algorithm like Boyer-Moore, so performance is O(n*m) in the worst case where n is the haystack length and m is the needle length.

Case Sensitivity

email = "user@example.com"

email.include?("example")  # => true
email.include?("EXAMPLE")  # => false
email.include?("@")        # => true

include? does a byte-by-byte comparison, so "A" and "a" are different characters. This trips up developers who come from languages where inclusion checks default to case-insensitive matching, but it also gives you predictable behaviour: the method never does anything behind your back that could change the meaning of the comparison. For case-insensitive checks, normalise both the source and the needle first:

email.downcase.include?("example")  # => true

Calling downcase on the string before checking inclusion works correctly but allocates a new string object. For a single check this is fine, but in a tight loop processing thousands of strings the allocation overhead can add up. Each downcase call creates a new string with all characters converted to lowercase, and while the allocation cost per call is small, the cumulative effect across thousands of iterations can create measurable GC pressure. Another way to compare without allocating a new object:

email.casecmp("example").zero?  # => false

casecmp returns 0 when the strings match case-insensitively, -1 or 1 otherwise, and nil when there’s no comparison possible. It avoids the downcase allocation entirely. Note that casecmp compares full strings, not substrings, so it checks whether the entire email matches "example" case-insensitively rather than whether the email contains "example" as a substring. Use it when you want a full-string comparison without allocation, not as a drop-in replacement for include?.

Guard Clauses

The most common real-world use is a quick guard in a method:

def process_command(input)
  return puts "Cancelled" if input.include?("quit")
  return puts "Stopping"  if input.include?("stop")
  # ...
end

process_command("please quit now")  # => Cancelled
process_command("start task")       # => (no early return)

This pattern is especially useful when processing user input or reading files line by line. Because the guard returns early as soon as the keyword is found, the method body below the guards never executes for matching inputs. This keeps the core logic clean and avoids deeply nested if/else chains that become hard to follow as the number of recognised keywords grows.

File type detection

filename = "report.pdf"

if filename.include?(".pdf")
  puts "Rendering PDF"
elsif filename.include?(".doc")
  puts "Opening Word document"
else
  puts "Unknown format"
end
# => Rendering PDF

When you need multiple extensions, chain the checks or reach for a regular expression. The include? approach breaks down if filenames can contain .pdf elsewhere (e.g. ".pdf.bak"), so use end_with? or match? for anything security-adjacent. A file named backup.pdf.bak would match include?(".pdf") because the substring appears in the middle, which is probably not what you want when the goal is to determine the actual file extension.

Edge Cases

Empty substring

"hello".include?("")   # => true
"".include?("")        # => true

An empty string is technically always found within any string. This follows from the mathematical definition of substring containment: the empty string is a substring of every string because you can find it at every position without consuming any characters. Writing code that depends on this behaviour is unusual, but it is worth knowing about because it means include?("") will never return false for any input, including an empty string itself. If your code path might produce an empty search needle, guard against it explicitly rather than relying on this edge-case behaviour to trigger a particular branch of logic.

Substring longer than the string

When the needle is longer than the haystack, include? returns false without raising an error. Ruby checks the lengths first because a shorter string cannot contain a longer one as a contiguous subsequence, making this case a constant-time determination that never reaches the character-scanning loop.

"hi".include?("hello")  # => false

Ruby does not raise an error when the substring is longer than the haystack. It simply returns false because a shorter string cannot contain a longer one as a contiguous sequence. This is consistent with the method’s contract: include? answers a yes-or-no question, and the answer is no when the needle is too long to fit. The check is O(1) in this case because Ruby can compare lengths first before scanning characters.

Nil argument

"hello".include?(nil)
# => NoMethodError: undefined method `include?' for nil:NilClass

Ruby does not coerce nil to a string here. If your argument might be nil, guard against it explicitly with a type check before the include? call. The guard tests the argument’s class rather than checking for nil specifically, which also handles other non-string types that would trigger the same error:

return false unless arg.is_a?(String)
str.include?(arg)

Ruby raises a NoMethodError when you pass nil to include? because the error actually occurs on the nil object rather than on the string. The method call nil.include? is what triggers the error: Ruby tries to dispatch the method to nil before it even evaluates the surrounding expression. The guard shown above prevents this by checking the argument’s type before the call reaches include?.

Non-string argument

"hello".include?(123)
# => wrong argument type (expected string) (ArgumentError)

Ruby raises an ArgumentError for non-string types rather than coerces them. If you need to check for numeric or other types, convert first with to_s. This strictness is deliberate: Ruby does not want to guess whether you meant to search for the textual representation of a number or made a mistake. The explicit conversion with to_s makes the intent visible in the code and prevents silent bugs where a non-string argument would produce an unexpected match or mismatch.

Relation to Array#include?

Both String and Array expose include?, but they check different things:

# String: checks substring presence
"hello".include?("ell")  # => true

# Array: checks element membership
["hello", "world"].include?("hello")  # => true
["hello", "world"].include?("ell")    # => false

The parameter types differ — Array checks for an object equal to one of its elements, String checks for a character sequence within itself.

See Also