ruby-concurrency/concurrent-ruby · error · NameError

no member '#{member}' in struct

Error message

no member '#{member}' in struct

What it means

When a non-Integer member is read, ns_get dispatches to the reader method via send(member); an undefined member raises NoMethodError, rescued and re-raised as NameError("no member 'x' in struct"). This matches core Struct semantics: only members declared at definition can be read, and there is no dynamic field support.

Source

Thrown at lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb:69

      # @!visibility private
      def ns_to_h
        length.times.reduce({}){|memo, i| memo[self.class::MEMBERS[i]] = @values[i]; memo}
      end

      # @!macro struct_get
      #
      # @!visibility private
      def ns_get(member)
        if member.is_a? Integer
          if member >= @values.length
            raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
          end
          @values[member]
        else
          send(member)
        end
      rescue NoMethodError
        raise NameError.new("no member '#{member}' in struct")
      end

      # @!macro struct_equality
      #
      # @!visibility private
      def ns_equality(other)
        self.class == other.class && self.values == other.values
      end

      # @!macro struct_each
      #
      # @!visibility private
      def ns_each
        values.each{|value| yield value }
      end

      # @!macro struct_each_pair
      #

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Check membership before dynamic reads: s.class.members.include?(key.to_sym)
  2. Use to_h when you need Hash-like tolerant access (to_h returns only declared members)
  3. Keep member lists in one constant shared between the definition and dynamic accessors

Example fix

# before
Cfg = Concurrent::ImmutableStruct.new(:host, :port)
c = Cfg.new('localhost', 8080)
v = c[:prot] # NameError

# after
key = :port if Cfg.members.include?(:port)
v = key && c[key]
Defensive patterns

Strategy: type-guard

Validate before calling

key = key.to_sym if key.is_a?(String)
struct[key] if struct.class.members.include?(key)

Type guard

def known_member?(struct_class, key)
  struct_class.members.include?(key.to_sym)
end

Try / catch

begin
  v = struct[key]
rescue NameError => e
  raise unless e.message.include?('no member')
  v = struct.to_h[key.to_sym] # nil for extras; schema drift surfaced
end

Prevention

When it happens

Trigger: s[:typo] or s.typo where the member was never declared or was renamed; symbol-vs-string key drift when forwarding params; de-serializing JSON into a struct assuming extra keys are readable.

Common situations: Renaming a member in the definition but not at read sites; schema drift between producer and consumer of the struct; using symbol literals copied from a different struct.

Related errors


AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21). Data as JSON: /api/errors/7a56ef728ec3733f. Report an issue: GitHub.