Project

General

Profile

Actions

Feature #20812

closed

Proposal for Safe Include Method in Ruby

Added by rogerconsul (Roger Consul) 14 days ago. Updated 14 days ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:119606]

Description

Proposal for Safe Include Method in Ruby

Description

Add a new method include_safe? to Ruby's Enumerable module that safely handles nil arguments in inclusion checks.

Problem Statement

The current include? method raises an error when passed nil as an argument. This requires developers to write defensive code with explicit nil checks, leading to less readable and more error-prone code.

# Current problematic scenarios:
collection.include?(nil)        # Works, checks for nil in collection
collection.include?(value)      # Raises error if value is nil

# Current workarounds:
value && collection.include?(value)
collection.include?(value.to_s)

Proposed Solution

Add include_safe? method that returns false when the argument is nil:

module Enumerable
  def include_safe?(obj)
    return false if obj.nil?
    include?(obj)
  end
end

Usage Examples

numbers = [1, 2, 3]
document_id = nil

# Current approach
document_id && numbers.include?(document_id)  # false

# Proposed approach
numbers.include_safe?(document_id)            # false

# Works normally for non-nil values
numbers.include_safe?(2)                      # true
numbers.include_safe?(4)                      # false

# Edge cases handled
[nil].include_safe?(nil)                      # false
[].include_safe?(nil)                         # false

Benefits

  1. Improved Safety: Eliminates a common source of runtime errors
  2. Better Readability: Removes need for explicit nil checks
  3. Consistent Behavior: Provides predictable handling of nil values
  4. Rails Alignment: Similar to Rails' safe navigation patterns
  5. Reduced Boilerplate: Eliminates common defensive coding patterns

Implementation Notes

This would be implemented in C as part of Ruby's core, but here's a Ruby reference implementation:

module Enumerable
  def include_safe?(obj)
    return false if obj.nil?
    include?(obj)
  end
end

Testing Strategy

require 'minitest/autorun'

class TestIncludeSafe < Minitest::Test
  def setup
    @array = [1, 2, 3]
    @array_with_nil = [1, nil, 3]
  end

  def test_basic_inclusion
    assert @array.include_safe?(1)
    refute @array.include_safe?(4)
  end

  def test_nil_handling
    refute @array.include_safe?(nil)
    refute @array_with_nil.include_safe?(nil)
  end

  def test_empty_collection
    refute [].include_safe?(nil)
    refute [].include_safe?(1)
  end
end

Alternative Considerations

  1. Name Alternatives:

    • try_include?
    • safe_include?
    • includes_safely?
  2. Alternative Approaches:

    • Modify existing include? behavior (rejected due to backward compatibility)
    • Add parameter to existing include? (rejected for clarity)

Impact Analysis

Positive Impact

  • Reduces runtime errors
  • Improves code readability
  • Follows principle of least surprise

Potential Concerns

  • Another method to learn
  • Possible confusion with regular include?

Similar Features in Ruby

  • Safe navigation operator (&.)
  • try method in Rails
  • fetch method with default value

Backward Compatibility

This change is fully backward compatible as it introduces a new method without modifying existing behavior.

Reference Implementation

A gem implementing this feature is available at: [Link to gem if created]

Questions for Core Team

  1. Is include_safe? the best name for this method?
  2. Should this be added to Array specifically rather than Enumerable?
  3. Should we consider any additional edge cases?

Updated by jeremyevans0 (Jeremy Evans) 14 days ago

rogerconsul (Roger Consul) wrote:

The current include? method raises an error when passed nil as an argument.

Are you sure?

# No Error
[1, 2].include?(nil)
[Object.new].include?(nil)

# Error, but due to custom ==, not include?
o = Object.new
def o.==(v) = (raise unless v)
[o].include?(nil)

Maybe you are using some core extension that causes include? to raise an error if given nil? Can you provide a self-contained reproducible example that shows include? raising if the argument is nil?

Updated by rogerconsul (Roger Consul) 14 days ago

jeremyevans0 (Jeremy Evans) wrote in #note-1:

rogerconsul (Roger Consul) wrote:

The current include? method raises an error when passed nil as an argument.

Are you sure?

# No Error
[1, 2].include?(nil)
[Object.new].include?(nil)

# Error, but due to custom ==, not include?
o = Object.new
def o.==(v) = (raise unless v)
[o].include?(nil)

Maybe you are using some core extension that causes include? to raise an error if given nil? Can you provide a self-contained reproducible example that shows include? raising if the argument is nil?

Well... After you mentioned it, I realized we were using version 2.7.5 🥲
Error didn't reproduce on newer versions. We can close this thread.

Thanks for your time, Jeremy <3

Actions #3

Updated by jeremyevans0 (Jeremy Evans) 14 days ago

  • Status changed from Open to Closed
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0