Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Non-throwing #to_x? between numeric types #9899

Open
HertzDevil opened this issue Nov 10, 2020 · 0 comments
Open

[RFC] Non-throwing #to_x? between numeric types #9899

HertzDevil opened this issue Nov 10, 2020 · 0 comments

Comments

@HertzDevil
Copy link
Contributor

HertzDevil commented Nov 10, 2020

Every integer conversion method from String, e.g. #to_i, has 3 variants:

class String
  def to_i(..., &)
    # ...
  end

  def to_i(...)
    to_i(...) { raise ... }
  end

  def to_i?(...)
    to_i(...) { nil }
  end
end

These variants perform range checks to determine whether the number fits into the target type's range. In contrast, number-to-number conversions only provide the throwing variant:

"128".to_i8 # ArgumentError
"128".to_i8? # => nil
128.to_i8 # OverflowError

I propose that the #to_x? methods should also be available in the numeric types, including the Big types. This would unify the numeric conversion interface between numbers and strings. Additionally, these methods would play very well with #9389 (the methods there only accept String and Char right now, since nothing else in stdlib defines them).

From the discussion on Gitter there are a few ways to approach this:

  • Make the convert primitive return nil in case of overflow, then implement the regular conversion methods as to_i? || raise ...; they in turn won't be primitives anymore. The block variant follows naturally, but isn't necessary (String itself doesn't have block variants for #to_f32 and #to_f64). This might have far consequences related to codegen.
  • Allow the convert primitive to either raise an exception or return nil, so that both #to_i and #to_i? are primitives.
  • Perform a range check, as numeric comparisons work across types:
    def to_i8?
      to_i8 if Int8::MIN <= self <= Int8::MAX
    end
    This should work for integer-to-integer conversions. I am not sure what overflow really means for floating-point types.
  • Catch any exceptions that result from the usual conversion:
    def to_i8?
      to_i8 rescue nil
    end
    I think this is the least preferable as it should be possible to determine whether a conversion overflows, without necessitating the use of exceptions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants