Main Content

Code Patterns for subsref and subsasgn Methods

For R2021b and later, the recommended process for customizing indexing is to use the modular indexing superclasses instead of overloading subsref and subsasgn. For more information, see Customize Object Indexing.

Customize Indexed Reference and Assignment

User-defined classes have the same indexing behaviors as that of built-in classes. Classes can customize indexing operations by overloading the functions that MATLAB® calls to evaluate indexing expressions. Overload the subsref and subsasgn functions when you want to define special behaviors for indexed reference and assignment.

Syntax for subsref and subsasgn Methods

MATLAB calls the subsref and subsasgn methods of your class with these arguments.

MethodInputOutput

b = subsref(obj,s)

  • obj — Object or object array used in indexing expression

  • s — Indexing structure

b — Result of indexing expression

obj = subsasgn(obj,s,b)

  • obj — Object or object array used in indexing expression

  • s — Indexing structure

  • b — Value being assigned

obj — Object or object array after assignment

Modifying Number of Arguments

If your class design requires that indexing operations return or assign a different number of values than the number defined by the default indexing operation, overload the numArgumentsFromSubscript function to control nargout for subsref and nargin for subsasgn. For more information and examples, see numArgumentsFromSubscript.

Indexing Structure Describes Indexing Expressions

The indexing structure contains information that describes the indexing expression. Class methods use the information in the indexing structure to evaluate the expression and implement custom behavior.

For example, the CustomIndex class defines a property that you can use in indexing expressions.

classdef CustomIndex
   properties
      DataArray
   end
end

Create an object and assign a 5-by-5 matrix created by the magic function to the DataArray property.

a = CustomIndex;
a.DataArray = magic(5);

This subscripted reference expression returns the first row of the 5-by-5 matrix.

a.DataArray(1,:)
ans =

    17    24     1     8    15

This expression assigns new values to the first row of the array stored in the DataArray property.

a.DataArray(1,:) = [1 2 3 4 5];

This assignment statement uses:

  • A '.' type reference

  • A property name following the dot (that is, DataArray)

  • A range of indices (1,:) within parentheses

The indexing structure contains this information in the type and subs fields.

Values of the Indexing Structure

When executing an indexing expression, MATLAB calls the class subsref or subsasgn method, if the class overloads these functions. One of the arguments passed to the method is the indexing structure. The indexing structure has two fields:

  • type — One of the three possible indexing types: '.', '()', '{}'

  • subs — A char vector with the property name or cell array of the indices used in the expression, including : and end.

If the indexing expression is a compound expression, then MATLAB passes an array of structures, one struct for each level of indexing. For example, in this expression:

a.DataArray(1,:)

the indexing structure array S has these values:

  • S(1).type is set to '.', indicating that the first indexing operation is a dot.

  • s(1).subs is set to the property name, 'DataArray'

The second level of indexing is in the second element of the indexing structure:

  • S(2).types is set to '()' indicating the second indexing operation is parentheses indexing

  • S(2).subs is set to a cell array containing the indices {[1],[:]}

Typical Patterns for Indexing Methods

To overload the subsref and subasgn functions:

  • Determine the full indexing expression using the types and subs fields of the indexing structure.

  • Implement the specialized behaviors for the indexing operations supported by the class.

  • Return the appropriate values or modified objects in response to the call by MATLAB.

A switch statement is a convenient way to detect the first level of indexing. There are three types of indexing—dot, parentheses, and braces. Each case block in the switch statement implements all indexing expressions that begin with that first-level type of indexing.

The methods must implement all indexing expressions that the class supports. If you do not customize a particular type of indexing, call the built-in function to handle that expression.

Use the length of the indexing structure array and indexing type define conditional statements for compound indexing expressions.

Code Framework for subsref Method

The following framework for the subsref method shows how to use information in the indexing structure in conditional statements. Your application can involve other expressions not shown here.

function varargout = subsref(obj,s)
   switch s(1).type
      case '.'
         if length(s) == 1
            % Implement obj.PropertyName
            ...
         elseif length(s) == 2 && strcmp(s(2).type,'()')
            % Implement obj.PropertyName(indices)
            ...
         else
            [varargout{1:nargout}] = builtin('subsref',obj,s);
         end
      case '()'
         if length(s) == 1
            % Implement obj(indices)
            ...
         elseif length(s) == 2 && strcmp(s(2).type,'.')
            % Implement obj(ind).PropertyName
            ...
         elseif length(s) == 3 && strcmp(s(2).type,'.') && strcmp(s(3).type,'()')
            % Implement obj(indices).PropertyName(indices)
            ...
         else
            % Use built-in for any other expression
            [varargout{1:nargout}] = builtin('subsref',obj,s);
         end
      case '{}'
         if length(s) == 1
            % Implement obj{indices}
            ...
         elseif length(s) == 2 && strcmp(s(2).type,'.')
            % Implement obj{indices}.PropertyName
            ...
         else
            % Use built-in for any other expression
            [varargout{1:nargout}] = builtin('subsref',obj,s);
         end
      otherwise
         error('Not a valid indexing expression')
   end

Using varargout for the returned value enables the method to work with object arrays. For example, suppose that you want to support the return of a comma-separated list with an expression like this one:

[x1,...xn] = objArray.PropertyName(Indices)

This expression results in a two-element indexing structure array. The first-level type is dot ('.') and the second level is parentheses ('()'). Build the varargout cell array with each value in the array.

case '.'
   ...
   if length(s)==2 && strcmp(s(2).type,'()')
      prop = s(1).subs;      % Property name
      n = numel(obj);        % Number of elements in array
      varargout = cell(1,n); % Preallocate cell array
      for k = 1:n
         varargout{k} = obj(k).(prop).(s(2).subs);
      end
   end
   ...
end

subsasgn Pattern

The following framework for the subsasgn method shows how to use the indexing structure in conditional statements that implement assignment operations.

function obj = subsasgn(obj,s,varargin)

   % Allow subscripted assignment to uninitialized variable
   if isequal(obj,[])
      % obj = ClassName.empty;
   end

   switch s(1).type
      case '.'
         if length(s) == 1
            % Implement obj.PropertyName = varargin{:};
            ...
         elseif length(s) == 2 && strcmp(s(2).type,'()')
            % Implement obj.PropertyName(indices) = varargin{:};
            ...
         else
            % Call built-in for any other case
            obj = builtin('subsasgn',obj,s,varargin{:});
         end
      case '()'
         if length(s) == 1
            % Implement obj(indices) = varargin{:};
         elseif length(s) == 2 && strcmp(s(2).type,'.')
            % Implement obj(indices).PropertyName = varargin{:};
            ...
         elseif length(s) == 3 && strcmp(s(2).type,'.') && strcmp(s(3).type,'()')
            % Implement obj(indices).PropertyName(indices) = varargin{:};
            ...
         else
            % Use built-in for any other expression
            obj = builtin('subsasgn',obj,s,varargin{:});
         end       
      case '{}'
         if length(s) == 1
            % Implement obj{indices} = varargin{:}
            ...
         elseif length(s) == 2 && strcmp(s(2).type,'.')
            % Implement obj{indices}.PropertyName = varargin{:}
            ...
            % Use built-in for any other expression
            obj = builtin('subsasgn',obj,s,varargin{:});
         end
      otherwise
         error('Not a valid indexing expression')
   end
end

Using varargin for the right-side value of the assignment statement enables the method to work with object arrays. For example, suppose that you want to support the assignment of a comma-separated list with an expression like this one:

C = {'one';'two';'three'};
[objArray.PropertyName] = C{:}

This expression results in an indexing structure with the dot type ('.') indexing The cell array C on the right side of the assignment statement produces a comma-separated list. This code assigns one list item to each property in the object array.

case '.'
   if length(s)==1
      prop = s(1).subs;      % Property name
      n = numel(obj);        % Number of elements in array
      for k = 1:n
         obj(k).(prop) = varargin{k};
      end
   end
end

Subscripted Assignment with an Uninitialized Variable

Assigning to an element of an uninitialized variable results in a call to the subsasgn method of the class on the right side of the assignment. For example, this class defines a subsasgn method that simply calls the built-in subsasgn method for parenthesis indexing.

classdef MyClass
   methods
      function obj = subsasgn(obj,s,varargin)
         switch s(1).type
            case '()'
               obj = builtin('subsasgn',obj,s,varargin{:});
         end
      end
   end
end

When attempting to assign an object of MyClass to the first element of the uninitialized variable, B(1) in the following statement, MATLAB calls the subsasgn method of MyClass with an empty double ([]) as the first argument. The assignment can cause an error because the subsasgn method must be passed an object of the class.

clear B
B(1) = MyClass;
The following error occurred converting from MyClass to double:
Conversion to double from MyClass is not possible.

Error in MyClass/subsasgn (line 6)
            obj = builtin('subsasgn',obj,s,varargin{:});

The subsasgn method can detect this situation and take the appropriate action, such as returning a useful error message if the class does not support this type of assignment, or converting the input to an object of the class and passing it to subsasgn.

For example, because MyClass can allow subscripted assignment to an uninitialized variable, the subsasgn method can change the first argument from the empty double to an empty MyClass object.

Use the isequal function to check the input and the empty static method to create the empty object.

classdef MyClass
    methods
        function obj = subsasgn(obj,s,varargin)
            if isequal(obj,[])
                obj = MyClass.empty;
            end
            obj = builtin('subsasgn',obj,s,varargin{:});
        end
    end
end

Subscripted assignment to an uninitialized variable now avoids the previous error.

clear B
B(1) = MyClass;
B = 

  MyClass with no properties.

Related Topics