From: kazuki@... Date: 2020-04-11T11:34:27+00:00 Subject: [ruby-core:97825] [Ruby master Bug#16660] Struct#deconstruct_keys inconsistent behavior Issue #16660 has been updated by ktsj (Kazuki Tsujimoto). Status changed from Open to Rejected My stance is "if we really should care about consistency over performance, we should remove the `keys` argument itself from the specification. Otherwise, we should do a thorough optimization using the `keys` argument(*)". Since `deconstruct_keys` is supposed to be used implicitly in the context of pattern matching rather than explicitly by the user, I don't think this inconsistency will actually cause confusion for the user. (*) This means that the return value of `deconstruct_keys` is implementation-dependent. --- > I believe that such optimization should be done within the pattern matching implementation, not a particular class API. Let's compare `Struct#deconstruct_keys` to `Hash#deconstruct_keys`. ```ruby # Struct#deconstruct_keys(palkan's version) s.deconstruct_keys([:a, :c]) #=> {a: 1} # Hash#deconstruct_keys(2.7's version) h = {a: 1, b: 2} h.deconstruct_keys([:a, :c]) #=> {a: 1, b: 2} ``` Should `h.deconstruct_keys([:a, :c])` also return `{a: 1}`? I don't think so. The optimal return value for each class is different. Therefore, we should implement optimized `#deconstruct_keys` in each class. > we can re-use the deconstruction hash for keys subsets or cache the result of the key presence check The return value can still be cached in the current specification. For example: ```ruby case val in {a:, b:, c:} in {a:, b:} end ``` We can compile this code as following pseudo code: ```ruby case val in {a:, b:} && {c:} in {a:, b:} end ``` ---------------------------------------- Bug #16660: Struct#deconstruct_keys inconsistent behavior https://bugs.ruby-lang.org/issues/16660#change-85047 * Author: palkan (Vladimir Dementyev) * Status: Rejected * Priority: Normal * Assignee: ktsj (Kazuki Tsujimoto) * ruby -v: ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux] * Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN ---------------------------------------- Here is an example of a kind of surprising (at least to me) `Struct#deconstruct_keys` behaviour: ```ruby klass = Struct.new(:a, :b) s = klass.new(1, 2) ``` 1) When some keys are not recognized and the total number of the keys is not greater than the size of the struct: ```ruby s.deconstruct_keys([:a, :c]) #=> {a: 1} ``` 2) When some keys are not recognized but the total number of the keys is greater than the size of the struct: ```ruby s.deconstruct_keys([:a, :b, :c]) #=> {} ``` It's not clear why the first one filters unknown keys and the latter one returns an empty Hash instead of the `{a: 1}`. This behaviour was introduced in https://github.com/ruby/ruby/commit/2439948bcc0ec9daf91cf79301195e59bad49aff. Prior to that change an empty hash was returned in both cases. -- https://bugs.ruby-lang.org/ Unsubscribe: