ruby-concurrency/concurrent-ruby · error · NameError
identifier #{name} needs to be constant
Error message
identifier #{name} needs to be constant What it means
When a String name is given to the struct factory, the library registers the generated class as a constant under the parent module via const_set. Ruby requires constant names to start with an uppercase letter; a lowercase name makes const_set fail with NameError, which the library rescues and re-raises as NameError("identifier x needs to be constant"). The name must be a valid constant identifier like 'Point', not 'point'.
Source
Thrown at lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb:156
end
# @!visibility private
def self.define_struct_class(parent, base, name, members, &block)
clazz = Class.new(base || Object) do
include parent
self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze)
def ns_initialize(*values)
raise ArgumentError.new('struct size differs') if values.length > length
@values = values.fill(nil, values.length..length-1)
end
end
unless name.nil?
begin
parent.send :remove_const, name if parent.const_defined?(name, false)
parent.const_set(name, clazz)
clazz
rescue NameError
raise NameError.new("identifier #{name} needs to be constant")
end
end
members.each_with_index do |member, index|
clazz.send :remove_method, member if clazz.instance_methods(false).include? member
clazz.send(:define_method, member) do
@values[index]
end
end
clazz.class_exec(&block) unless block.nil?
clazz.singleton_class.send :alias_method, :[], :new
clazz
end
end
end
end
View on GitHub (pinned to 0b88d5ff75)
Solutions
- Capitalize before passing: name ? name.to_s.split('_').map(&:capitalize).join : nil
- Or simpler: name&.capitalize when a single word
- Skip the name entirely (pass no String) and assign the returned class to your own constant
Example fix
# before
Concurrent::MutableStruct.new('line_item', :sku, :qty)
# after
Concurrent::MutableStruct.new('LineItem', :sku, :qty)
# or keep it anonymous
LineItem = Concurrent::MutableStruct.new(:sku, :qty) Defensive patterns
Strategy: validation
Validate before calling
name = name.split('_').map(&:capitalize).join if name.is_a?(String)
Concurrent::MutableStruct.new(name, *members) Type guard
def constant_like?(name) name.is_a?(String) && name.match?(/\A[A-Z][A-Za-z0-9_]*\z/) end
Prevention
- Camelize/capitalize dynamically generated names before passing them
- Prefer anonymous definitions assigned to your own constants
- Add a spec for your factory with a snake_case input
When it happens
Trigger: Concurrent::MutableStruct.new('point', :x, :y) (lowercase first letter); names built dynamically from snake_case strings, e.g. "#{type}_struct"; names containing characters illegal in constants (dashes, spaces).
Common situations: Generating struct classes from table names or config keys that arrive snake_case; porting code that used Object.const_set with the same raw string; DSLs that accept arbitrary user-supplied type names.
Related errors
- no member '#{member}' in struct
- no member '#{member}' in struct
- no member '#{member}' in struct
- offset #{member} too large for struct(size:#{@values.length}
- struct size differs
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/de933ff46e829948.
Report an issue: GitHub.