Weird Ruby: Positive and Negative Strings
Recently I saw the following Ruby code:
content = +"\n"
content << "## #{title}\n"
content << "\n"
content
My initial reaction was WTF! The +
before "\n"
seemed like some
potential typo to me. A moment later I was enlightened and I told
myself - “Wow! That’s weird enough for Weird Ruby!”. And here we are:
Turns out that when frozen string
literals
were introduced in Ruby 2.3 a couple of unary methods were added to
the String
class - namely unary +
and -
. They made it possible
to have “positive” and “negative” string literals. What does this mean
exactly? Let’s figure this out together!
Positive Strings
Let’s first take a look at String#+@
. Here’s its description from
the official API
docs:
If the string is frozen, then return duplicated mutable string. If the string is not frozen, then return the string itself.
Simple enough, right? Here are a couple of examples to help illustrate this:
# frozen_string_literal: true
foo = "string"
foo.upcase!
# => FrozenError - can't modify frozen String
(+foo).upcase!
# => "STRING"
What’s the practical application of this weird notation? I think the number one usage would to be to give you an unfrozen string that you can use as the receiver for mutating string methods (e.g. concatenation):
# frozen_string_literal: true
# Doesn't work because you can't modify a frozen string literal
content = "\n"
content << "## #{title}\n"
content << "\n"
content
# Common workaround
content = "\n".dup
content << "## #{title}\n"
content << "\n"
content
# The new method in action
content = +"\n"
content << "## #{title}\n"
content << "\n"
content
# A non-destructive alternative
content = "\n"
content += "## #{title}\n"
content += "\n"
content
You’ll be the judge of how useful this is for you. Concatenation is obviously more memory efficient, but in simple scenarios you don’t gain much by using it.
Negative Strings
Now, let’s turn our attention to String#-@
. Here’s its description from
the official API
docs:
If the string is frozen, then return the string itself. If the string is not frozen, return a frozen, possibly pre-existing copy of it.
And here are a few examples:
foo = "string"
foo.upcase!
# => "STRING"
(-foo).downcase!
# => FrozenError - can't modify frozen String
Generally, I don’t think you’ll find any use for it if you’re leveraging the frozen_string_literals
pragma, but it’s
an useful shorthand to freeze a string otherwise.
Closing Thoughts
A word of caution - the positive and negative string literals result in code that’s pretty weird, so you should use them sparingly.
Until next time! Keep hacking!
Articles in the Series
- Weird Ruby: Pure Object-Oriented Negation
- Weird Ruby: Positive and Negative Strings
- Weird Ruby: Double Negation
- Weird Ruby: Single-quoted Heredocs
- Weird Ruby: Block Comments
- Weird Ruby: Zeroing in on a Couple of Numeric Predicates
- Weird Ruby: Invoking Lambdas
- Weird Ruby: For Loops
- Weird Ruby: Array Multiplication
- Weird Ruby: Heredoc Delimiters
- Weird Ruby: Mixing Code and Data
- Weird Ruby: A Weird Way to Filter Out Elements
- Weird Ruby: Nil Conversions
- Weird Ruby: Fun with String#split
- Weird Ruby: Incrementing Strings
- Weird Ruby: The Double Aliased Enumerable Method