Idiosyncratic Ruby: Try Converting
source link: https://idiosyncratic-ruby.com/54-try-converting.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Similar to metaprogramming, Ruby's type conversion system has evolved over time. While the result functions, it is also a little inconsistent and suffers from poor naming. Let's put things in perspective:
Implicit and Explicit Conversion
Ruby objects are usually converted to other classes/types using to_*
functions. For example, converting the String "42"
to a Float is done with to_f
:
"42".to_f # => 42.0
This process is called explicit conversion (you want the object converted).
Some classes provide a second way to convert an object: implicit conversion (you expect the object to be of some type).
The following table shows the defined conversion methods of Ruby's important core classes:
Class
Explicit Conversion
Implicit Conversion
Array
to_a
to_ary
Hash
to_h
to_hash
String
to_s
to_str
Integer
to_i
to_int
Float
to_f
-
Complex
to_c
-
Rational
to_r
-
Regexp
-
to_regexp
IO
-
to_io
In most cases you should just go with the explicit conversion method and you are good.
Expecting a Specific Type
However, things are more complicated in real life. You might, and many of Ruby's core methods do, expect a compatible type that you can deal with. That is where two kinds of indirect conversion methods come into play:
.try_convert
Class Methods (Implicit Conversion or nil
)
Some of Ruby's core classes have a try_convert
class method. Although its not obvious, which classes have one, and which do not have one (Array
has one, Integer
does not), its semantics are pretty clear: It will convert the object into an instance of the class via the implicit conversion method (e.g. to_ary
), or, if no implicit conversion method is defined, will just return nil
. It will also return nil
, if the result of the conversion is nil
, or nil
was given as argument:
Array.try_convert(42) # => nil
Array.try_convert(42..43) # => nil
Array.try_convert([42, 43]) # => [42, 43]
Array.try_convert(nil) # => nil
o = Object.new
def o.to_ary() [42] end
Array.try_convert(o) # => [42]
Uppercased Kernel Methods (Special Conversion or TypeError
)
Idiosyncratically, there is a third way of converting values: Uppercased Kernel methods, like Array()
.¹ The objects you pass in will be converted to the corresponding class, following the following rules:
- If there is a special conversion, apply it. See the table below for details and the exact application order (e.g.
Array()
does its special conversion after it tried the two steps below). - Unless special conversion gets applied, try to convert via the implicit conversion method
- If it does not exist, try to convert via the explicit conversion method
- Raise a TypeError² if everything of the above has failed
¹ Although defining uppercased methods for your custom classes to create instances of it looks like an interesting idea at first glance, it is rather confusing. Consider defining class.[]
instead, which enables a very similar syntax, but uses the real constant it belongs to. An example of such usage is Set.
² Since Ruby 2.6, you can pass in a exception: false
keyword argument to return nil
instead of raising an error
Core Classes Conversion Table
Class
.try_convert
Kernel Method
Kernel Method w/ nil
Kernel Method Special
Array
Array.try_convert
Array()
Array(nil) # => []
If :to_ary
and :to_a
did not return an array, it will create single-element array which contains the given value
Hash
Hash.try_convert
Hash()
Hash(nil) # => {}
Hash([]) # => {}
. Also remember that you can convert arrays to hashes with Hash.[].
String
String.try_convert
String()
String(nil) # => ""
-
Integer
-
Integer()
Integer(nil) # TypeError
Special behavior for strings: Instead of calling String#to_i, it will be more rigid². Takes a second argument defining the numerical base. Also see ³
Float
-
Float()
Float(nil) # TypeError
- ³
Complex
-
Complex()
Complex(nil) # TypeError
-
Rational
-
Rational()
Rational(nil) # TypeError
-
Regexp
Regexp.try_convert
-
-
-
IO
IO.try_convert
-
-
-
² It will only convert strings that contain exactly an integer. It would not accept "42A", which String#to_i
would happily take. It also accepts radix prefixes and numbers that contain underscores, so basically it accepts the same format that is valid for direct integer literals in Ruby. Will raise an ArgumentError
if an invalid string is passed in.
³ It will convert to other low-level numerical types (such as integers and floats) directly, so Float(4)
will not call 4.to_f
When to Use What?
- Prefer explicit conversion:
to_*
methods - Use
.try_convert
for implicit conversion - Use the uppercased Kernel methods for their special effects, like
Array()
for wrapping single arguments in an arrays, orInteger()
for strict integer conversion
More Idiosyncratic Ruby
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK