[前][次][番号順一覧][スレッド一覧]

ruby-changes:70878

From: Jeremy <ko1@a...>
Date: Sat, 15 Jan 2022 05:18:13 +0900 (JST)
Subject: [ruby-changes:70878] a93cc3e23b (master): Make Hash#shift return nil for empty hash

https://git.ruby-lang.org/ruby.git/commit/?id=a93cc3e23b

From a93cc3e23b4044762e80820fc7a45606587e11db Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Mon, 27 Dec 2021 14:41:43 -0800
Subject: Make Hash#shift return nil for empty hash

Fixes [Bug #16908]
---
 NEWS.md                           |  6 ++++++
 hash.c                            |  7 +++----
 spec/ruby/core/hash/shift_spec.rb | 42 +++++++++++++++++++++++++++++----------
 test/ruby/test_hash.rb            | 10 +++++-----
 4 files changed, 46 insertions(+), 19 deletions(-)

diff --git a/NEWS.md b/NEWS.md
index cf79b059e5f..6b50bfd8651 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -51,6 +51,11 @@ Note that each entry is kept to a minimum, see links for details. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L51
 
 Note: We're only listing outstanding class updates.
 
+* Hash
+    * Hash#shift now always returns nil if the hash is
+      empty, instead of returning the default value or
+      calling the default proc. [[Bug #16908]]
+
 * Module
     * Module.used_refinements has been added. [[Feature #14332]]
     * Module#refinements has been added. [[Feature #12737]]
@@ -137,6 +142,7 @@ The following deprecated APIs are removed. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L142
 [Feature #15231]: https://bugs.ruby-lang.org/issues/15231
 [Bug #15928]:     https://bugs.ruby-lang.org/issues/15928
 [Feature #16131]: https://bugs.ruby-lang.org/issues/16131
+[Bug #16908]:     https://bugs.ruby-lang.org/issues/16908
 [Feature #17351]: https://bugs.ruby-lang.org/issues/17351
 [Feature #17391]: https://bugs.ruby-lang.org/issues/17391
 [Bug #17545]:     https://bugs.ruby-lang.org/issues/17545
diff --git a/hash.c b/hash.c
index 9b1f7624140..f032ef642a1 100644
--- a/hash.c
+++ b/hash.c
@@ -2445,7 +2445,7 @@ shift_i_safe(VALUE key, VALUE value, VALUE arg) https://github.com/ruby/ruby/blob/trunk/hash.c#L2445
 
 /*
  *  call-seq:
- *    hash.shift -> [key, value] or default_value
+ *    hash.shift -> [key, value] or nil
  *
  *  Removes the first hash entry
  *  (see {Entry Order}[#class-Hash-label-Entry+Order]);
@@ -2454,8 +2454,7 @@ shift_i_safe(VALUE key, VALUE value, VALUE arg) https://github.com/ruby/ruby/blob/trunk/hash.c#L2454
  *    h.shift # => [:foo, 0]
  *    h # => {:bar=>1, :baz=>2}
  *
- *  Returns the default value if the hash is empty
- *  (see {Default Values}[#class-Hash-label-Default+Values]).
+ *  Returns nil if the hash is empty.
  */
 
 static VALUE
@@ -2494,7 +2493,7 @@ rb_hash_shift(VALUE hash) https://github.com/ruby/ruby/blob/trunk/hash.c#L2493
 	    }
 	}
     }
-    return rb_hash_default_value(hash, Qnil);
+    return Qnil;
 }
 
 static int
diff --git a/spec/ruby/core/hash/shift_spec.rb b/spec/ruby/core/hash/shift_spec.rb
index 9d43e640f59..ea36488a04d 100644
--- a/spec/ruby/core/hash/shift_spec.rb
+++ b/spec/ruby/core/hash/shift_spec.rb
@@ -30,23 +30,45 @@ describe "Hash#shift" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/hash/shift_spec.rb#L30
     h.should == {}
   end
 
-  it "calls #default with nil if the Hash is empty" do
-    h = {}
-    def h.default(key)
-      key.should == nil
-      :foo
+  ruby_version_is '3.2' do
+    it "returns nil if the Hash is empty" do
+      h = {}
+      def h.default(key)
+        raise
+      end
+      h.shift.should == nil
+    end
+  end
+
+  ruby_version_is ''...'3.2' do
+    it "calls #default with nil if the Hash is empty" do
+      h = {}
+      def h.default(key)
+        key.should == nil
+        :foo
+      end
+      h.shift.should == :foo
     end
-    h.shift.should == :foo
   end
 
   it "returns nil from an empty hash" do
     {}.shift.should == nil
   end
 
-  it "returns (computed) default for empty hashes" do
-    Hash.new(5).shift.should == 5
-    h = Hash.new { |*args| args }
-    h.shift.should == [h, nil]
+  ruby_version_is '3.2' do
+    it "returns nil for empty hashes with defaults and default procs" do
+      Hash.new(5).shift.should == nil
+      h = Hash.new { |*args| args }
+      h.shift.should == nil
+    end
+  end
+
+  ruby_version_is ''...'3.2' do
+    it "returns (computed) default for empty hashes" do
+      Hash.new(5).shift.should == 5
+      h = Hash.new { |*args| args }
+      h.shift.should == [h, nil]
+    end
   end
 
   it "preserves Hash invariants when removing the last item" do
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index f79879c20a5..39376524fad 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -1048,14 +1048,14 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L1048
     h = @cls.new {|hh, k| :foo }
     h[1] = 2
     assert_equal([1, 2], h.shift)
-    assert_equal(:foo, h.shift)
-    assert_equal(:foo, h.shift)
+    assert_nil(h.shift)
+    assert_nil(h.shift)
 
     h = @cls.new(:foo)
     h[1] = 2
     assert_equal([1, 2], h.shift)
-    assert_equal(:foo, h.shift)
-    assert_equal(:foo, h.shift)
+    assert_nil(h.shift)
+    assert_nil(h.shift)
 
     h =@cls[1=>2]
     h.each { assert_equal([1, 2], h.shift) }
@@ -1066,7 +1066,7 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L1066
     def h.default(k = nil)
       super.upcase
     end
-    assert_equal("FOO", h.shift)
+    assert_nil(h.shift)
   end
 
   def test_reject_bang2
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]