All about coding

Ruby and Rails technical content written by Lucian Ghinda

Ruby: What does && and || return?

#ruby #programming-tips #coding #programming

How do these things work?

Have you ever wondered how the following code works? Specifically, how come the key will have another value than true or false.

key = loaded_key || Key.find(params[:key])

or maybe how does the simple memorization pattern work:

def key
  @key ||= Key.find(params[:key])
end

The answer is in the idea that || and && do not return the response asTrueClass or FalseClass objects (or, more precisely, do not return the global value true or false) but it returns one operand or the other.

What is false and what is true in Ruby

nil and false are the only falsy values.

Everything else is considered truthy (source)

What does && return

x && y
# returns x if x is false
# returns y if x is true

A simple truth table might look like this:

# +────────+─────────────+─────────+
# | X      | Y           | Return  |
# +────────+─────────────+─────────+
# | false  | true/false  | X       |
# | true   | true        | Y       |
# | true   | false       | Y       |
# +────────+─────────────+─────────+

But as in Ruby, we have the concept of falsy values, let’s expand this table:

# +──────────+─────────+─────────+─────────+
# | X        | Y       | Result  | Return  |
# +──────────+─────────+─────────+─────────+
# | nil      | Truthy  | nil     | X       |
# | nil      | Falsy   | nil     | X       |
# | false    | Truthy  | false   | X       |
# | false    | nil     | false   | X       |
# | true     | Falsy   | Falsy   | Y       |
# | Truthy   | Truthy  | Truthy  | Y       |
# +──────────+─────────+─────────+─────────+

When evaluating object_a && object_b Ruby will return:

You can check that by running the examples I added in this Github gist: https://gist.github.com/lucianghinda/2677e6549c9e5927bd88d9c646524564

What does || return

x || y 
# returns x if x is true without evaluating y
# returns y if x is false

Let’s try to put this in a kind of truth table:

# +───────+─────────────+─────────+
# | X     | Y           | X || Y  |
# +───────+─────────────+─────────+
# | true  | true/false  | X       |
# | false | true/false  | Y       |
# +───────+─────────────+─────────+

And then to expand it by taking into consideration the concept of truthy and falsy from Ruby:

# +────────+─────────+─────────+─────────+
# | X      | Y       | Result  | Return  |
# +────────+─────────+─────────+─────────+
# | nil    | Truthy  | Truthy  | Y       |
# | nil    | Falsy   | Falsy   | Y       |
# | false  | Truthy  | Truthy  | Y       |
# | false  | Falsy   | Falsy   | Y       |
# | Truthy | Falsy   | Truthy  | X       |
# | Truthy | Truthy  | Truthy  | X       |
# +────────+─────────+─────────+─────────+

We can then say that object_a || object_b will return:

Explaining how the ||= work

Coming back to the question asked at the beginning, how does the following piece of code work:

key = loaded_key || Key.find(params[:key])

We can read it like this:

What about memoization:

def key
  @key ||= Key.find(params[:key])
end

First this code is actually a shorthand for:

def key
  @key || @key = Key.find_by_key(params[:key])
end
There are two pieces of knowledge that we need to explain this along with how   works thoroughly:

“Undefined instance variable returns nil” (source)

“a   = b approximatively translates to a   a = b and not a = a   b” (source)

What happens when key method is called first:

Now let’s run this a second time. What happens then?

It depends on the previous result ofKey.find_by_key(params[:key]).

If a Key was found, then the initial expression will be evaluated as Truthy || Object and return the Truthy object that in this case is @key:

@key || @key =  Key.find_by_key(params[:key])

# when @key is not falsy is returning:

@key

If a Key was not found and the result of find_by_key is nil then the second run will be evaluates as Falsy || Object and return the Object:

@key || @key = Key.find_by_key(params[:key])

# when @key is falsy is returning and executing the initialize of @key:

@key = Key.find_by_key(params[:key])

More examples:

Transforming true in 1 and false in 0

# This method transforms true to 1 and false to 0
def transform(x)
x && 1 || 0
end

(source for the idea to convert boolean to integers using short-circuit operators)

When x is true:

true && 1 || 0 => (true && 1) || 0 => 1 || 0 => 1

When x is false:

false && 1 || 0 => (false && 1) || 0 => false || 0 => 0`

Responding with one object or another if argument is true

def success(response)
  puts "Success"
end

def error
  puts "Error"
end

def render(response)
   response && success(response) || error
end

render(nil) #=> "Error"
render(false) #=> "Error"
render(true) # => "Success"
render(1) #=> "Success"

View posts by tag

#ruby #concurrency #general-advice #life-hack #ruby-on-rails #google-cloud #kubernetes #bash #apis #rest-api #guide #docker #bugs-and-errors #howtos #newbie #programming-languages #programming-tips #til #coding #learning #postgresql #sql #design-patterns #web-api #programming-blogs #programmer #databases #programming #learn-coding #learning-journey #side-project #coding-challenge #design-principles #solid-principles #git #tips #testing #test-driven-development #test-automation #code #shortpost #refactoring #code-review #rails #developer #documentation #focus #blogging #content-creation #share #advice #newsletter #active-record #ai #ai-tools #generative-ai #web-development #errors #cursor-ide #ides #gpt-4 #news #scalability #tailwind-css #chatgpt #opensource #reviews #startups #github #technology #types #sorbet #performance #rubymine #vscode #codesnippet #features #syntax #friendlyrb #conference #directories #writing #summary #animation #articles #podcasts #publishing #patterns #pattern-matching #content #updates #seo-for-developers #marketing #properties #gems #matching #rspec #llm #tools #lsp #claudeai #opencode #mcp #rubocop #quality #planning #products #mvp #upgrade #agentic-ai #skills #superpowers