Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rand(Range) - no implicit conversion of Range into Integer

Tags:

ruby

A follow up on the question How to create a random time between a range .

Kernel#rand works with Time range:

require 'time'
rand(Time.parse('9 am')..Time.parse('11:30 am'))

But when I tried with a custom class, I ended up with the error:

`rand': no implicit conversion of Range into Integer (TypeError)

class Int
  include Comparable

  attr_reader :num

  def initialize(num)
    @num = num
  end

  def succ
    Int.new(num + 1)
  end

  def <=>(other)
    num <=> other.num
  end

  def to_s
    "Int(#{num})"
  end

  def to_int
    @num
  end

  alias_method :inspect, :to_s
end

puts rand(Int.new(1)..Int.new(3))

Why? What am I missing in the custom class? Can we use such a custom class in rand(Range)?

like image 523
Arie Xiao Avatar asked Jan 17 '18 03:01

Arie Xiao


1 Answers

I don't know of any documentation for what specifically Kernel#rand expects from a Range argument but we can get a look at what's going on by overriding respond_to? in your class and then watching as things fall apart:

def respond_to?(m)
  puts "They want us to support #{m}"
  super
end

Doing that tells us that rand wants to call the #- and #+ methods on your Int instances. This does make some sense given that rand(a..b) is designed for working with integers.

So we throw in quick'n'dirty implementations of addition and subtraction:

def -(other)
  self.class.new(to_int - other.to_int)
end

def +(other)
  self.class.new(to_int + other.to_int)
end

and we start getting rand Ints out of our calls to rand.


I'm not sure where (or if) this is documented so you'll have to excuse a bit of hand waving. I normally spend some time rooting around the Ruby source code to answer this sort of question but I lack the time right now.

like image 57
mu is too short Avatar answered Oct 10 '22 02:10

mu is too short