Structures
A struct is a MATLAB record: a value with named fields, each of which holds an array of any class. Fields are added by assignment, accessed by dot notation, and enumerated with fieldnames. A struct array is an array whose elements are structs sharing the same field names. Structs are the conventional MATLAB record type — the analogue of C structs and Python dataclasses — and are everywhere in interfaces with the standard library, in figure properties, in optimisation options, and in user code that does not (yet) merit a class.
Constructing a struct
The principal forms are literal and constructor:
% Literal — by assignment to fields
s.name = "Ada";
s.birthYear = 1815;
s.skills = ["analysis", "programming"];
% Constructor
s2 = struct('name', "Ada", 'birthYear', 1815, 'skills', ["analysis", "programming"]);
The two forms are equivalent. The struct constructor accepts name-value pairs and is the most common form in code that constructs structs programmatically. A field assignment creates the field if it does not already exist; a read of a non-existent field is an error.
Field access
The dot operator selects a field:
s.name % "Ada"
s.birthYear % 1815
s.skills(2) % "programming"
The dynamic field reference s.(expr) evaluates expr as a string and uses the result as a field name; it permits programmatic access:
fname = "birthYear";
s.(fname) % 1815
for f = fieldnames(s).' % returns a cell column; transpose for iteration
name = f{1};
fprintf("%s = ", name)
disp(s.(name))
end
The dynamic form is the right way to write code whose field names come from a variable; it should be preferred over eval for that purpose.
Struct arrays
A struct array is a 1-d or multi-d array of structs that share the same field names. The most reliable constructor is the assigned form:
arr(1).name = "Ada";
arr(1).year = 1815;
arr(2).name = "Grace";
arr(2).year = 1906;
arr(3).name = "Donald";
arr(3).year = 1938;
size(arr) % [1 3]
class(arr) % 'struct'
{arr.name} % {"Ada", "Grace", "Donald"}
[arr.year] % [1815 1906 1938]
The constructor form with cell-valued arguments builds a struct array directly:
arr = struct('name', {"Ada", "Grace", "Donald"}, ...
'year', {1815, 1906, 1938});
A cell as an argument to struct is interpreted as the field values across the struct array. A scalar argument (not a cell) is replicated. The rule is a small surprise: struct('x', {1 2 3}) builds a 1×3 struct array, but struct('x', [1 2 3]) builds a 1×1 struct whose x field is [1 2 3].
The comma-separated-list form arr.name produces, for a length-N struct array, N separate values — exactly the same expansion idiom as cells with {:}. Brackets gather them: {arr.name} builds a cell, [arr.year] concatenates them.
Enumerating fields
fieldnames(s) returns a column cell of strings; isfield(s, 'name') tests presence; rmfield(s, 'name') returns a copy with the field removed; orderfields(s) reorders fields alphabetically (or by an explicit permutation).
fieldnames(s)
% 3×1 cell:
% 'name'
% 'birthYear'
% 'skills'
isfield(s, 'name') % true
s2 = rmfield(s, 'skills');
The number of fields is numel(fieldnames(s)); the size of the struct array is size(s) — the two are unrelated.
Nested structs
Structs nest arbitrarily. Field access uses chained dots; the dynamic form chains likewise:
person.name = "Ada";
person.address.city = "London";
person.address.year = 1815;
person.address.city % "London"
person.address.(fname_var) % dynamic-field on the inner struct
A common pattern in standard-library functions is the options struct: a function takes a single struct whose fields configure its behaviour. The optimset, odeset, and statset functions in their respective toolboxes build such structs.
Practical idioms
Storing per-record state. A struct array is the right container for a small, homogeneous list of records — say, a few hundred sensor readings:
for k = 1:numel(arr)
arr(k).processed = process(arr(k).raw);
end
For larger or more heterogeneous tabular data, a table (the next page) is usually clearer.
Bundling outputs. A function with many outputs can return a single struct rather than a long argument list:
function result = analyse(x)
result.mean = mean(x);
result.std = std(x);
result.n = numel(x);
result.histogram = histcounts(x, 20);
end
The caller writes r = analyse(x); r.mean and is spared a long [mu, sigma, n, h] = analyse(x) call. The convention is widespread.
Configuration. A struct is a natural configuration record. The defaults are set once; the caller overrides specific fields. The helper setfield(s, 'name', value) is the function-form equivalent of s.name = value and is useful for programmatic construction.
struct vs class
A classdef class (the OOP page) offers methods, inheritance, property validation, and explicit handle-vs-value semantics. A struct offers none of these. For data with simple structure and no behaviour, a struct is the lighter and more idiomatic choice; for anything with invariants, behaviour, or polymorphism, a class is more appropriate. The path is one-way: a struct can be converted to a class, but a class instance is not a struct.
A note on cell-valued struct fields
A struct field may itself be a cell. To assign a single cell to a field (rather than letting struct(...) interpret it as the per-element values), wrap it once:
s = struct('x', {{1 2 3}}); % s.x is a 1×3 cell
size(s) % [1 1]
class(s.x) % 'cell'
% Compare:
t = struct('x', {1 2 3}); % a 1×3 struct array, each with scalar x
size(t) % [1 3]
The doubled brace is the idiomatic and somewhat ugly fix for this pitfall.