The first chapter of Understanding Computation does a whirlwind tour of Ruby programming - syntax, control structures, etc. One of the constructs the author calls out is the Struct
class. I've always used it like this:
That is, I'd use the Struct
constructor as a factory and assign the result to a constant. Then, if I needed to, I'd reopen the class that had just been created in order to add methods.
The example in the book does it a different way:
This creates an anonymous Struct
, uses it as a superclass, assigns it to a constant, and reopens the new class all in one fell swoop. I poked around various gems and this seems like a pretty common approach - for example, Unicorn::App::Inetd::CatBody
does this, and so does Arel::Attributes::Attribute
.
The latter approach does result in more noise in the ancestor chain:
That's because Struct.new
returns an anonymous subclass of Struct
, and that subclass hasn't been assigned to a constant, so Ruby has to synthesize a to_s
value from the type and object id. You could get around that using the two-arg constructor, but that creates the new class as a constant in the class Struct
, which is a little weird:
Sometimes I see code create Struct
subclasses using the class
keyword without adding methods; for example from WebSocket::Driver
:
Is there a reason to do the above rather than OpenEvent = Struct.new(nil)
?
Edit: Bruno Michel noted that the Struct
constructor also accepts a block:
This is a nice technique because it results in a clean ancestor chain.
Edit 2: Tom Stuart (the author of 'Understanding Computation') pointed out this great writeup on Structs by James Edward Grey II where he explains why he's using Structs in the way he does. Definitely worth a read, especially with Ara Howard weighing in with a comment about his Map
implementation.
Thanks to Thomas Olausson for reviewing this post!