Issue #12732 has been updated by Yui NARUSE.


Below is PoC; it may have a path which raises an exception.

```diff
diff --git a/object.c b/object.c
index 05bef4d..5d63803 100644
--- a/object.c
+++ b/object.c
@@ -2750,17 +2750,60 @@ static VALUE
 rb_f_integer(int argc, VALUE *argv, VALUE obj)
 {
     VALUE arg = Qnil;
+    VALUE opts = Qnil;
+    VALUE exception = Qnil;
+    VALUE vbase = Qundef;
     int base = 0;
+    static ID int_kwds[1];
 
-    switch (argc) {
-      case 2:
-	base = NUM2INT(argv[1]);
-      case 1:
-	arg = argv[0];
-	break;
-      default:
-	/* should cause ArgumentError */
-	rb_scan_args(argc, argv, "11", NULL, NULL);
+    rb_scan_args(argc, argv, "11:", &arg, &vbase, &opts);
+    if (!NIL_P(vbase)) {
+	base = NUM2INT(vbase);
+    }
+    if (!NIL_P(opts)) {
+	if (!int_kwds[0]) {
+	    int_kwds[0] = rb_intern_const("exception");
+	}
+	if (rb_get_kwargs(opts, int_kwds, 0, 1, &exception)) {
+	    VALUE tmp;
+	    if (RB_FLOAT_TYPE_P(arg)) {
+		double f;
+		if (base != 0) goto arg_error;
+		f = RFLOAT_VALUE(arg);
+		if (FIXABLE(f)) return LONG2FIX((long)f);
+		return rb_dbl2big(f);
+	    }
+	    else if (RB_INTEGER_TYPE_P(arg)) {
+		if (base != 0) goto arg_error;
+		return arg;
+	    }
+	    else if (RB_TYPE_P(arg, T_STRING)) {
+		const char *s;
+		long len;
+		rb_must_asciicompat(arg);
+		RSTRING_GETMEM(arg, s, len);
+		tmp = rb_cstr_parse_inum(s, len, NULL, base);
+		if (NIL_P(tmp)) {
+		    return exception;
+		}
+		return tmp;
+	    }
+	    else if (NIL_P(arg)) {
+		if (base != 0) goto arg_error;
+		return exception;
+	    }
+	    if (base != 0) {
+		tmp = rb_check_string_type(arg);
+		if (!NIL_P(tmp)) return rb_str_to_inum(tmp, base, TRUE);
+arg_error:
+		rb_raise(rb_eArgError, "base specified for non string value");
+	    }
+	    tmp = convert_type(arg, "Integer", "to_int", FALSE);
+	    if (NIL_P(tmp)) {
+		return rb_to_integer(arg, "to_i");
+	    }
+	    return tmp;
+	}
     }
     return rb_convert_to_integer(arg, base);
 }
```
```ruby
def assert(a, b)
  if a != b
    raise "'#{a}' != '#{b}'"
  end
end
def assert_raise(ex)
  begin
    yield
    raise "#{ex} is expected but not raised"
  rescue ex
    # correct
  rescue
    raise "#{ex} is expected but #{$!.inspect}"
  end
end
o = Object.new
assert 123, Integer("123")
assert 50, Integer("32", 16)
assert 16, Integer("10", 16, exception: o)
assert o, Integer("x", exception: o)
assert o, Integer("x", 16, exception: o)
assert_raise(ArgumentError){ Integer("x") }
assert_raise(ArgumentError){ Integer("x", 16) }
```
```ruby
require'benchmark/ips'
Benchmark.ips{|x|
  x.report("rescue") {
    Integer('foo') rescue nil
  }
  x.report("kwarg") {
    Integer('foo', exception: nil)
  }
}
```
```
Warming up --------------------------------------
              rescue    36.258k i/100ms
               kwarg    64.004k i/100ms
Calculating -------------------------------------
              rescue    392.926k ( 8.9%) i/s -      1.958M in   5.025204s
               kwarg    844.563k (14.9%) i/s -      4.096M in   5.017539s
```

----------------------------------------
Feature #12732: An option to pass to `Integer`, `Float`, to return `nil` instead of raise an exception
https://bugs.ruby-lang.org/issues/12732#change-61713

* Author: Aaron Patterson
* Status: Feedback
* Priority: Normal
* Assignee: Yukihiro Matsumoto
----------------------------------------
I would like to be able to pass an option to `Integer()` and `Float()` so that they don't raise an exception, but return `nil` instead.  For example:

~~~
Integer(string, exception: false)
~~~

The reason I want this function is so that I can convert strings from YAML or JSON to integers if they parse correctly, or just return strings if they can't be parsed.

---Files--------------------------------
integer-parse.pdf (29 KB)


-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>