Bug #18470
closedUnion of two identical sets produces a set with duplicate members
Description
We came across an issue where the union of two identical sets produced a non uniq Set.
We noticed this when upgrading from 2.7.1 to 3.1
See the attached test, the last assertion fails
C = Struct.new :id
a = Set.new
b = Set.new
f = C.new
a << f
f.id = 1
b << f
a + b
# => #<Set: {#<struct C id=1>, #<struct C id=1>}>
b + a
# => #<Set: {#<struct C id=1>}>
(a + b).uniq
=> [#<struct C id=1>]
Files
Updated by byroot (Jean Boussier) almost 4 years ago
The tricky part is that the object is mutated after being inserted, so it's sounds like a rehash no longer happens, possibly because of https://bugs.ruby-lang.org/issues/16996.
It may or may not be considered a bug, I'll see what it would take to fix it though.
Also this might have more its place on https://github.com/ruby/set/issues
Updated by byroot (Jean Boussier) almost 4 years ago
So it's indeed a rehash issue: https://github.com/casperisfine/set/commit/40ff2f907118d8766217bbe8ac27119111050217
Also it seems @marcandre (Marc-Andre Lafortune) knew about this case (or similar), based on the description of the issue I linked.
The main problem is that Set doesn't expose a rehash method, so there's not really any way to workaround it. Maybe it should?
Updated by smokinggun (John Weir) almost 4 years ago
The main problem is that
Setdoesn't expose arehashmethod, so there's not really any way to workaround it. Maybe it should?
The rehash works. Should add also have a rehash?
C = Struct.new :id
a = Set.new
f = C.new
a << f
f.id = 1
a << f
# => #<Set: {#<struct C id=1>, #<struct C id=1>}>
Updated by ufuk (Ufuk Kayserilioglu) almost 4 years ago
Doesn't the following from https://bugs.ruby-lang.org/issues/16996 address this issue?
...
Today, it is official that sets with elements that are later mutated must beSet#reset, so it is official that this should not be relied upon.
...
It seems like this is not a bug and the outcome is expected without a call to Set#reset:
C = Struct.new :id
a = Set.new
b = Set.new
f = C.new
a << f
f.id = 1
b << f
a.reset
# => #<Set: {#<struct C id=1>}>
a + b
# => #<Set: {#<struct C id=1>}>
b + a
# => #<Set: {#<struct C id=1>}>
Updated by byroot (Jean Boussier) almost 4 years ago
Set#reset
Ah, that's the rehash equivalent I didn't see.
Yeah, I think we can close.
Updated by byroot (Jean Boussier) almost 4 years ago
- Status changed from Open to Closed