class Struct - Documentation for Ruby 3.5 (original) (raw)

Class Struct provides a convenient way to create a simple class that can store and fetch values.

This example creates a subclass of Struct, Struct::Customer; the first argument, a string, is the name of the subclass; the other arguments, symbols, determine the members of the new subclass.

Customer = Struct.new('Customer', :name, :address, :zip) Customer.name
Customer.class
Customer.superclass

Corresponding to each member are two methods, a writer and a reader, that store and fetch values:

methods = Customer.instance_methods false methods

An instance of the subclass may be created, and its members assigned values, via method ::new:

joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe

The member values may be managed thus:

joe.name
joe.name = 'Joseph Smith' joe.name

And thus; note that member name may be expressed as either a string or a symbol:

joe[:name]
joe[:name] = 'Joseph Smith, Jr.' joe['name']

See Struct::new.

What’s Here

First, what’s elsewhere. Class Struct:

See also Data, which is a somewhat similar, but stricter concept for defining immutable value objects.

Here, class Struct provides methods that are useful for:

Methods for Creating a Struct Subclass

Methods for Querying

Methods for Comparing

Methods for Fetching

Methods for Assigning

Methods for Iterating

Methods for Converting

Public Class Methods

Source

def self.json_create(object) new(*object['v']) end

See as_json.

Source

static VALUE rb_struct_s_keyword_init_p(VALUE obj) { }

Returns true if the class was initialized with keyword_init: true. Otherwise returns nil or false.

Examples:

Foo = Struct.new(:a) Foo.keyword_init? Bar = Struct.new(:a, keyword_init: true) Bar.keyword_init? Baz = Struct.new(:a, keyword_init: false) Baz.keyword_init?

Source

static VALUE rb_struct_s_members_m(VALUE klass) { VALUE members = rb_struct_s_members(klass);

return rb_ary_dup(members);

}

Returns the member names of the Struct descendant as an array:

Customer = Struct.new(:name, :address, :zip) Customer.members

Source

static VALUE rb_struct_s_def(int argc, VALUE *argv, VALUE klass) { VALUE name = Qnil, rest, keyword_init = Qnil; long i; VALUE st; VALUE opt;

argc = rb_scan_args(argc, argv, "0*:", NULL, &opt);
if (argc >= 1 && !SYMBOL_P(argv[0])) {
    name = argv[0];
    --argc;
    ++argv;
}

if (!NIL_P(opt)) {
    static ID keyword_ids[1];

    if (!keyword_ids[0]) {
        keyword_ids[0] = rb_intern("keyword_init");
    }
    rb_get_kwargs(opt, keyword_ids, 0, 1, &keyword_init);
    if (UNDEF_P(keyword_init)) {
        keyword_init = Qnil;
    }
    else if (RTEST(keyword_init)) {
        keyword_init = Qtrue;
    }
}

rest = rb_ident_hash_new();
RBASIC_CLEAR_CLASS(rest);
for (i=0; i<argc; i++) {
    VALUE mem = rb_to_symbol(argv[i]);
    if (rb_is_attrset_sym(mem)) {
        rb_raise(rb_eArgError, "invalid struct member: %"PRIsVALUE, mem);
    }
    if (RTEST(rb_hash_has_key(rest, mem))) {
        rb_raise(rb_eArgError, "duplicate member: %"PRIsVALUE, mem);
    }
    rb_hash_aset(rest, mem, Qtrue);
}
rest = rb_hash_keys(rest);
RBASIC_CLEAR_CLASS(rest);
OBJ_FREEZE(rest);
if (NIL_P(name)) {
    st = anonymous_struct(klass);
}
else {
    st = new_struct(name, klass);
}
setup_struct(st, rest);
rb_ivar_set(st, id_keyword_init, keyword_init);
if (rb_block_given_p()) {
    rb_mod_module_eval(0, 0, st);
}

return st;

}

Struct.new returns a new subclass of Struct. The new subclass:

The new subclass has its own method ::new; thus:

Foo = Struct.new('Foo', :foo, :bar) f = Foo.new(0, 1)

Class Name

With string argument class_name, returns a new subclass of Struct named Struct::_classname_:

Foo = Struct.new('Foo', :foo, :bar) Foo.name
Foo.superclass

Without string argument class_name, returns a new anonymous subclass of Struct:

Struct.new(:foo, :bar).name

Block

With a block given, the created subclass is yielded to the block:

Customer = Struct.new('Customer', :name, :address) do |new_class| p "The new subclass is #{new_class}" def greeting "Hello #{name} at #{address}" end end
dave = Customer.new('Dave', '123 Main') dave dave.greeting

Output, from Struct.new:

"The new subclass is Struct::Customer"

Member Names

Symbol arguments member_names determines the members of the new subclass:

Struct.new(:foo, :bar).members
Struct.new('Foo', :foo, :bar).members

The new subclass has instance methods corresponding to member_names:

Foo = Struct.new('Foo', :foo, :bar) Foo.instance_methods(false) f = Foo.new
f.foo
f.foo = 0
f.bar
f.bar = 1
f

Singleton Methods

A subclass returned by Struct.new has these singleton methods:

Keyword Argument

By default, the arguments for initializing an instance of the new subclass can be both positional and keyword arguments.

Optional keyword argument keyword_init: allows to force only one type of arguments to be accepted:

KeywordsOnly = Struct.new(:foo, :bar, keyword_init: true) KeywordsOnly.new(bar: 1, foo: 0)

KeywordsOnly.new(0, 1)

PositionalOnly = Struct.new(:foo, :bar, keyword_init: false) PositionalOnly.new(0, 1)

PositionalOnly.new(bar: 1, foo: 0)

Any = Struct.new(:foo, :bar, keyword_init: nil) Any.new(foo: 1, bar: 2)

Any.new(1, 2)

Public Instance Methods

Source

static VALUE rb_struct_equal(VALUE s, VALUE s2) { if (s == s2) return Qtrue; if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse; if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse; if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { rb_bug("inconsistent struct"); /* should never happen */ }

return rb_exec_recursive_paired(recursive_equal, s, s2, s2);

}

Returns true if and only if the following are true; otherwise returns false:

Examples:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr == joe joe_jr[:name] = 'Joe Smith, Jr.'

joe_jr == joe

Source

VALUE rb_struct_aref(VALUE s, VALUE idx) { int i = rb_struct_pos(s, &idx); if (i < 0) invalid_struct_pos(s, idx); return RSTRUCT_GET(s, i); }

Returns a value from self.

With symbol or string argument name given, returns the value for the named member:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe[:zip]

Raises NameError if name is not the name of a member.

With integer argument n given, returns self.values[n] if n is in range; see Array Indexes at Array:

joe[2]
joe[-2]

Raises IndexError if n is out of range.

Source

VALUE rb_struct_aset(VALUE s, VALUE idx, VALUE val) { int i = rb_struct_pos(s, &idx); if (i < 0) invalid_struct_pos(s, idx); rb_struct_modify(s); RSTRUCT_SET(s, i, val); return val; }

Assigns a value to a member.

With symbol or string argument name given, assigns the given value to the named member; returns value:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe[:zip] = 54321 joe

Raises NameError if name is not the name of a member.

With integer argument n given, assigns the given value to the n-th member if n is in range; see Array Indexes at Array:

joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe[2] = 54321
joe[-3] = 'Joseph Smith' joe

Raises IndexError if n is out of range.

Source

def as_json(*) klass = self.class.name klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!" { JSON.create_id => klass, 'v' => values, } end

Methods Struct#as_json and Struct.json_create may be used to serialize and deserialize a Struct object; see Marshal.

Method Struct#as_json serializes self, returning a 2-element hash representing self:

require 'json/add/struct' Customer = Struct.new('Customer', :name, :address, :zip) x = Struct::Customer.new.as_json

Method JSON.create deserializes such a hash, returning a Struct object:

Struct::Customer.json_create(x)

Source

static VALUE rb_struct_deconstruct_keys(VALUE s, VALUE keys) { VALUE h; long i;

if (NIL_P(keys)) {
    return rb_struct_to_h(s);
}
if (UNLIKELY(!RB_TYPE_P(keys, T_ARRAY))) {
    rb_raise(rb_eTypeError,
             "wrong argument type %"PRIsVALUE" (expected Array or nil)",
             rb_obj_class(keys));

}
if (RSTRUCT_LEN(s) < RARRAY_LEN(keys)) {
    return rb_hash_new_with_size(0);
}
h = rb_hash_new_with_size(RARRAY_LEN(keys));
for (i=0; i<RARRAY_LEN(keys); i++) {
    VALUE key = RARRAY_AREF(keys, i);
    int i = rb_struct_pos(s, &key);
    if (i < 0) {
        return h;
    }
    rb_hash_aset(h, key, RSTRUCT_GET(s, i));
}
return h;

}

Returns a hash of the name/value pairs for the given member names.

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) h = joe.deconstruct_keys([:zip, :address]) h

Returns all names and values if array_of_names is nil:

h = joe.deconstruct_keys(nil) h

Source

static VALUE rb_struct_dig(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); self = rb_struct_lookup(self, *argv); if (!--argc) return self; ++argv; return rb_obj_dig(argc, argv, self, Qnil); }

Finds and returns an object among nested objects. The nested objects may be instances of various classes. See Dig Methods.

Given symbol or string argument name, returns the object that is specified by name and identifiers:

Foo = Struct.new(:a) f = Foo.new(Foo.new({b: [1, 2, 3]})) f.dig(:a) f.dig(:a, :a) f.dig(:a, :a, :b) f.dig(:a, :a, :b, 0) f.dig(:b, 0)

Given integer argument n, returns the object that is specified by n and identifiers:

f.dig(0) f.dig(0, 0) f.dig(0, 0, :b) f.dig(0, 0, :b, 0) f.dig(:b, 0)

Source

static VALUE rb_struct_each(VALUE s) { long i;

RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size);
for (i=0; i<RSTRUCT_LEN(s); i++) {
    rb_yield(RSTRUCT_GET(s, i));
}
return s;

}

Calls the given block with the value of each member; returns self:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.each {|value| p value }

Output:

"Joe Smith" "123 Maple, Anytown NC" 12345

Returns an Enumerator if no block is given.

Related: each_pair.

Source

static VALUE rb_struct_each_pair(VALUE s) { VALUE members; long i;

RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size);
members = rb_struct_members(s);
if (rb_block_pair_yield_optimizable()) {
    for (i=0; i<RSTRUCT_LEN(s); i++) {
        VALUE key = rb_ary_entry(members, i);
        VALUE value = RSTRUCT_GET(s, i);
        rb_yield_values(2, key, value);
    }
}
else {
    for (i=0; i<RSTRUCT_LEN(s); i++) {
        VALUE key = rb_ary_entry(members, i);
        VALUE value = RSTRUCT_GET(s, i);
        rb_yield(rb_assoc_new(key, value));
    }
}
return s;

}

Calls the given block with each member name/value pair; returns self:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.each_pair {|(name, value)| p "#{name} => #{value}" }

Output:

"name => Joe Smith" "address => 123 Maple, Anytown NC" "zip => 12345"

Returns an Enumerator if no block is given.

Related: each.

Source

static VALUE rb_struct_eql(VALUE s, VALUE s2) { if (s == s2) return Qtrue; if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse; if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse; if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { rb_bug("inconsistent struct"); /* should never happen */ }

return rb_exec_recursive_paired(recursive_eql, s, s2, s2);

}

Returns true if and only if the following are true; otherwise returns false:

Related: Object#==.

Source

static VALUE rb_struct_hash(VALUE s) { long i, len; st_index_t h; VALUE n;

h = rb_hash_start(rb_hash(rb_obj_class(s)));
len = RSTRUCT_LEN(s);
for (i = 0; i < len; i++) {
    n = rb_hash(RSTRUCT_GET(s, i));
    h = rb_hash_uint(h, NUM2LONG(n));
}
h = rb_hash_end(h);
return ST2FIX(h);

}

Returns the integer hash value for self.

Two structs of the same class and with the same content will have the same hash code (and will compare using Struct#eql?):

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.hash == joe_jr.hash joe_jr[:name] = 'Joe Smith, Jr.' joe.hash == joe_jr.hash

Related: Object#hash.

Source

static VALUE rb_struct_inspect(VALUE s) { return rb_exec_recursive(inspect_struct, s, rb_str_new2("#<struct ")); }

Returns a string representation of self:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.inspect

Also aliased as: to_s

Source

static VALUE rb_struct_members_m(VALUE obj) { return rb_struct_s_members_m(rb_obj_class(obj)); }

Returns the member names from self as an array:

Customer = Struct.new(:name, :address, :zip) Customer.new.members

Related: to_a.

Source

static VALUE rb_struct_select(int argc, VALUE *argv, VALUE s) { VALUE result; long i;

rb_check_arity(argc, 0, 0);
RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size);
result = rb_ary_new();
for (i = 0; i < RSTRUCT_LEN(s); i++) {
    if (RTEST(rb_yield(RSTRUCT_GET(s, i)))) {
        rb_ary_push(result, RSTRUCT_GET(s, i));
    }
}

return result;

}

With a block given, returns an array of values from self for which the block returns a truthy value:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) a = joe.select {|value| value.is_a?(String) } a a = joe.select {|value| value.is_a?(Integer) } a

With no block given, returns an Enumerator.

Source

VALUE rb_struct_size(VALUE s) { return LONG2FIX(RSTRUCT_LEN(s)); }

Returns the number of members.

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.size

Source

static VALUE rb_struct_to_a(VALUE s) { return rb_ary_new4(RSTRUCT_LEN(s), RSTRUCT_CONST_PTR(s)); }

Returns the values in self as an array:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.to_a

Related: members.

Source

static VALUE rb_struct_to_h(VALUE s) { VALUE h = rb_hash_new_with_size(RSTRUCT_LEN(s)); VALUE members = rb_struct_members(s); long i; int block_given = rb_block_given_p();

for (i=0; i<RSTRUCT_LEN(s); i++) {
    VALUE k = rb_ary_entry(members, i), v = RSTRUCT_GET(s, i);
    if (block_given)
        rb_hash_set_pair(h, rb_yield_values(2, k, v));
    else
        rb_hash_aset(h, k, v);
}
return h;

}

Returns a hash containing the name and value for each member:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) h = joe.to_h h

If a block is given, it is called with each name/value pair; the block should return a 2-element array whose elements will become a key/value pair in the returned hash:

h = joe.to_h{|name, value| [name.upcase, value.to_s.upcase]} h

Raises ArgumentError if the block returns an inappropriate value.

Source

def to_json(*args) as_json.to_json(*args) end

Returns a JSON string representing self:

require 'json/add/struct' Customer = Struct.new('Customer', :name, :address, :zip) puts Struct::Customer.new.to_json

Output:

{"json_class":"Struct","t":{'name':'Rowdy',"age":null}}

Source

static VALUE rb_struct_values_at(int argc, VALUE *argv, VALUE s) { return rb_get_values_at(s, RSTRUCT_LEN(s), argc, argv, struct_entry); }

Returns an array of values from self.

With integer arguments integers given, returns an array containing each value given by one of integers:

Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.values_at(0, 2)
joe.values_at(2, 0)
joe.values_at(2, 1, 0) joe.values_at(0, -3)

Raises IndexError if any of integers is out of range; see Array Indexes at Array.

With integer range argument integer_range given, returns an array containing each value given by the elements of the range; fills with nil values for range elements larger than the structure:

joe.values_at(0..2)

joe.values_at(-3..-1)

joe.values_at(1..4)

Raises RangeError if any element of the range is negative and out of range; see Array Indexes at Array.