Struct attribute name from variable

The last line, prs.field (obviously) does not work. Can I somehow make call a method where the name of the method comes from a variable?

struct Person
  getter name : String
  getter email : String

  def initialize(@name, @email)
  end
end

prs = Person.new("Foo Bar", "foo@bar.com")
p! prs
p! prs.name
p! prs.email

["name", "email"].each {|field|
  #puts field
  prs.field
}

No, Crystal doesn’t have dynamic method invocation. Your best best would be doing something like:

case field
when "name" then prs.name
when "email" then prs.email
end

or something along those lines. However I’d argue theres probably a better way…

At runtime this is not possible. But it would work with a compile time variable in a macro:

{% for field in ["name", "email"] %}
  prs.{{ field.id }}
{% end %}
1 Like

Combining the two, you could use a macro that iterates over the ivars of a type to construct the case. This ofc only would work in the context of a method and assuming that you never need to do like prs.some_obj.some_value.

Yeah, this solution with a macro works really well.

Out of curiosity, what are you trying to do that uses this? Because ofc the macro could is literally the same as if you just did:

prs.name
prs.email

Then you don’t even need any macro code or anything…

EDIT: I.e. given that you need to manually specify what values to read in the macro, why not just manually call those methods on the object?

There are 4 attributes in my current real code and I run a regex on all 4 for a test to verify the values look good. I could of course do what you suggest and explicitly write down all 4, but it seemed to be too repetitive.

I dunno, hardly seems worth it to bring in macros just to save typing 4 lines. But I don’t know the real context so if that helps then :+1:. Do remember readability/simplicity go a long way in maintainability.

1 Like