Question

Check for Non-Nil and Non-Zero Values in Ruby

Question

How can I idiomatically check in Ruby that discount is neither nil nor zero?

if discount != nil && discount != 0
  # ...
end

Is there a clearer or more idiomatic way to write this condition?

Short Answer

You will learn how Ruby treats nil, false, and 0 in conditions, and how to write a concise, safe check for a numeric value that must be present and non-zero.

Concept

Ruby conditions use truthiness:

  • Only nil and false are falsy.
  • Everything else is truthy, including 0, empty strings (""), and empty arrays ([]).

That means this does not exclude zero:

if discount
  # This runs when discount is 0.
end

For a numeric discount, you must explicitly test whether it is zero. Ruby numeric objects provide zero? and nonzero? for this purpose.

A concise modern option is:

if discount&.nonzero?
  # discount exists and is not zero
end

nonzero? returns nil when the number is zero, and returns the number itself when it is non-zero. The safe-navigation operator (&.) prevents an error when discount is nil.

Mental Model

Think of an if statement as a door that opens only for truthy values.

In Ruby, nil and false are red stop signs, so the door stays closed. But 0 is not a stop sign in Ruby—it is a normal truthy value.

To reject zero, you need a second check: ask the number whether it is zero with zero?, or ask it whether it is non-zero with nonzero?.

Take Quiz

Syntax and Examples

For a value expected to be numeric, these are common choices.

# Clear and compatible with older Ruby versions
if discount && !discount.zero?
  puts "Apply the discount"
end

discount && ... works because Ruby does not evaluate the right side when discount is nil. This prevents calling zero? on nil.

# Concise modern Ruby
if discount&.nonzero?
  puts "Apply the discount"
end

The safe-navigation operator returns nil if discount is nil. If it is 0, nonzero? also returns nil. Both cases make the if body skip.

discount = 15
puts "Apply" if discount&.nonzero?

# Prints: Apply

You can also write the original comparison more idiomatically as:

if !discount.nil? && discount != 0
  # ...
end

However, discount && !discount.zero? or discount&.nonzero? better communicates that the value is expected to be a number.

Step by Step Execution

Consider this code:

discount = 10

if discount&.nonzero?
  puts "Discount is valid"
end

Step by step:

  1. discount contains 10.
  2. &. checks whether discount is nil. It is not, so Ruby calls 10.nonzero?.
  3. 10.nonzero? returns 10.
  4. 10 is truthy in Ruby.
  5. The condition passes and Ruby prints Discount is valid.

Now with zero:

discount = 0
puts "Discount is valid" if discount&.nonzero?
  1. discount is not nil, so Ruby calls 0.nonzero?.
  2. 0.nonzero? returns nil.
  3. nil is falsy, so nothing is printed.

Now with no value:

discount = nil
puts "Discount is valid" if discount&.nonzero?
  1. discount is nil.
  2. &. does not call nonzero?.
  3. The expression evaluates to nil.
  4. The condition is false, so nothing is printed.

Real World Use Cases

Checking for a present, non-zero number is useful when a zero value means “do not perform this action.”

  • Shopping carts: apply a discount only when the discount amount is non-zero.
  • Invoices: show a late fee only when its calculated amount is non-zero.
  • Analytics: calculate a ratio only when a count is non-zero.
  • API payloads: include an optional numeric setting only when it has a meaningful value.
  • Batch jobs: skip a database update when the number of changes is zero.

Example:

shipping_credit = order.shipping_credit

if shipping_credit&.nonzero?
  total -= shipping_credit
end
Take Quiz

Real Codebase Usage

In real Ruby applications, developers often use a guard clause to handle invalid or irrelevant values early:

def apply_discount(total, discount)
  return total unless discount&.nonzero?

  total - discount
end

This avoids wrapping the main logic in a large if block.

When input may come from a form, JSON request, or environment variable, validate or convert it before using numeric methods:

def parse_discount(value)
  Integer(value, exception: false)
end

raw_discount = params[:discount]
discount = parse_discount(raw_discount)

return "Invalid discount" if discount.nil?
return "No discount to apply" if discount.zero?

For domain rules, make the rule explicit. For example, a negative discount may be invalid even though it is non-zero:

return unless discount&.positive?

apply_discount(discount)

Use the condition that expresses the actual business rule: non-zero, positive, non-negative, or present.

Common Mistakes

Assuming 0 is false

In some languages, zero is falsy. In Ruby, it is truthy.

discount = 0
puts "Runs" if discount

This prints Runs.

Avoid it: explicitly use zero?, nonzero?, positive?, or another numeric predicate.

Calling zero? on nil

This raises an error:

discount = nil
puts "Valid" if !discount.zero?
# NoMethodError: undefined method `zero?' for nil

Avoid it: check first with discount && !discount.zero?, or use discount&.nonzero?.

Using != nil unnecessarily

if discount != nil
  # ...
end

This works, but Ruby developers typically write:

if discount
  # ...
end

When false must be distinguished from nil, use nil? explicitly. For a numeric discount, false is usually not a valid value and should be validated separately if it can occur.

Using nonzero? with non-numeric input

discount = "10"
discount&.nonzero?
# NoMethodError: undefined method `nonzero?' for "10"

Avoid it: convert external input to a number before testing it.

discount = Integer("10", exception: false)
puts "Valid" if discount&.nonzero?

Comparisons

Expressionnil010Best use
if discountfalsetruetrueCheck only whether a value is truthy
if discount && !discount.zero?falsefalsetrueClear numeric check; broadly compatible
if discount&.nonzero?falsefalsetrueConcise numeric check in modern Ruby
if !discount.nil? && discount != 0falsefalsetrueExplicit, but more verbose
if discount&.positive?falsefalsetrueRequire a value greater than zero

zero? returns true or false:

0.zero?  # true
5.zero?  # false

nonzero? returns the number or nil:

0.nonzero?  # nil
5.nonzero?  # 5

Because a non-zero number is truthy, nonzero? fits naturally in an if condition.

Take Quiz

Cheat Sheet

# Present and non-zero numeric value
if discount && !discount.zero?
  # ...
end

# Equivalent concise form (modern Ruby)
if discount&.nonzero?
  # ...
end

# Require a positive value, not merely non-zero
if discount&.positive?
  # ...
end

# Run code unless the value is nil or zero
unless discount.nil? || discount.zero?
  # ...
end
  • Ruby falsy values: nil and false only.
  • 0 is truthy in Ruby.
  • zero? and nonzero? are numeric methods.
  • Use &. when the receiver may be nil.
  • Convert untrusted strings to numbers before calling numeric predicates.

FAQ

Is 0 false in Ruby?

No. In Ruby, 0 is truthy. Only nil and false are falsy.

What is the most idiomatic Ruby check for nil and zero?

For a numeric value, discount&.nonzero? is concise. discount && !discount.zero? is also clear and widely used.

Why does nonzero? work directly in an if statement?

It returns nil for zero and returns the numeric receiver for a non-zero value. nil is falsy, while a number is truthy.

Should I use discount != nil in Ruby?

Usually no. Use if discount when any truthy value is acceptable. Use discount.nil? only when you specifically need to test for nil.

What happens if discount is false?

discount&.nonzero? returns false, because safe navigation only short-circuits for nil; it then evaluates as falsy in if. If false is possible input, validate the input type explicitly.

Can I use nonzero? with a string such as "0"?

No. Convert it first, for example with Integer(value, exception: false) or Float(value, exception: false).

Should a discount be non-zero or positive?

Use nonzero? when negative values are meaningful in your domain. Use positive? when only values greater than zero are valid.

Take Quiz

Mini Project

Description

Build a small method that calculates an order total after applying an optional discount. It demonstrates how to ignore a missing or zero discount while keeping the business rule readable.

Goal

Return the original total for a nil or zero discount, and subtract a non-zero discount.

Requirements

  1. Create a method named total_after_discount.
  2. Accept total and discount as arguments.
  3. Return total unchanged when discount is nil or zero.
  4. Subtract the discount when it is non-zero.
  5. Demonstrate the method with nil, zero, and non-zero discounts.
Take Quiz

Keep learning

Related questions

View more