Best practice for passing structure to a function and adding new fields when returning it
Show older comments
This is more a style question than anything else. I'm passing a structure to a function, adding some fields to the structure, and returning the structure. Which of the following is considered best practice?
Option 1: defining the fields you want to add as function outputs, passing the structure to the function, then adding each field from the function outputs.
[structa.multiplied, structa.divided, ...
structa.added, structa.subtracted, someExtraVar] = testFunction(structa, n);
function [multiplied, divided, added, subtracted, someExtraVar] = testFunction(structa,num)
multiplied=structa.mat1.*structa.mat2;
divided=structa.mat3./structa.mat4;
added=structa.mat5+structa.mat6;
subtracted=structa.mat6-structa.mat6;
someExtraVar=length(structa.mat6)*num;
end
or option 2: modifying the structure within the function
[structa, someExtraVar] = testFunction(structa);
function [structa, someExtraVar] = testFunction(structa, num)
structa.multiplied=structa.mat1.*structa.mat2;
structa.divided=structa.mat3./structa.mat4;
structa.added=structa.mat5+structa.mat6;
structa.subtracted=structa.mat6-structa.mat6;
someExtraVar=length(structa.mat6)*num;
end
Option 1 seems better to me, since the second option is modifying globals locally within function. The first option also tells you exactly what outputs the function returns without having to actually read the function. But I do have several functions which add multiple fields to a structure and it gets cumbersome to add four different fields from function outputs to a structure. It also does affect readability when a single line of code goes on forever.
Is there an accepted best practice for this/does anyone have any opinions or suggestions?
(This is dummy code, my actual structures and functions are more complex and I didn't want to distract from the question!)
Edit: a very simple snippet from my codebase because I think the above example isn't super clear about my actual usecase (I don't love the if switch, I might rewrtie that part later)
Option 1:
function [dat.filteredSignal,dat.filteredAtEvents,dat.muFSNew,...
dat.sigmaFSNew,dat.muFSAtEvents,dat.sigmaFSAtEvents, ...
dat.muFS, dat.sigmaFS] = normalizeFilteredSignals(dat,params)
assert(or(params.normalizeFS==0,params.normalizeFS==1), 'normalize FS parameter must exist and be 1 or 0');
filteredSignal=convertVarLengthCells2Mat(dat.filteredSignalEnsemble);
filteredAtEvents=convertVarLengthCells2Mat(dat.filteredSignalAtEventEnsemble);
[muFS,sigmaFS] = meanAndSigma(filteredSignal);
if params.normalizeFS==1
normFunc = @(x) (x-muFS)/sigmaFS ;
filteredSignal=normFunc(filteredSignalAll);
filteredAtEvents=normFunc(filteredAtEvents);
[muFSNew,sigmaFSNew] = meanAndSigma(filteredSignal);
else
muFSNew=muFS;
sigmaFSNew=sigmaFS;
end
[muFSAtEvents,sigmaFSAtEvents] = meanAndSigma(filteredAtEvents);
end
function [meanDat,sigmaDat] = meanAndSigma(inputVec)
meanDat=mean(inputVec,'omitnan');
sigmaDat=std(inputVec,0,'all','omitnan');
end
Option 2:
function [dat] = normalizeFilteredSignals(dat,params)
assert(or(params.normalizeFS==0,params.normalizeFS==1), 'normalize FS parameter must exist and be 1 or 0');
filteredSignal=convertVarLengthCells2Mat(dat.filteredSignalEnsemble);
filteredAtEvents=convertVarLengthCells2Mat(dat.filteredSignalAtEventEnsemble);
[dat.muFS,dat.sigmaFS] = meanAndSigma(filteredSignal);
if params.normalizeFS==1
normFunc = @(x) (x-muFS)/sigmaFS ;
dat.filteredSignal=normFunc(filteredSignalAll);
dat.filteredAtEvents=normFunc(filteredAtEvents);
[dat.muFSNew,dat.sigmaFSNew] = meanAndSigma(filteredSignal);
else
dat.muFSNew=muFS;
dat.sigmaFSNew=sigmaFS;
dat.filteredSignal=filteredSignal;
dat.filteredAtEvents=filteredAtEvents;
end
[dat.muFSAtEvents,dat.sigmaFSAtEvents] = meanAndSigma(filteredAtEvents);
end
function [meanDat,sigmaDat] = meanAndSigma(inputVec)
meanDat=mean(inputVec,'omitnan');
sigmaDat=std(inputVec,0,'all','omitnan');
end
8 Comments
I'd prefer this (i.e. making changes to the structure only in the calling program), but it's a matter of taste.
structa.mat1 = 23;
structa.mat2 = 3;
structa.mat3 = 12;
structa.mat4 = -3;
structa.mat5 = -pi;
structa.mat6 = exp(1);
num = 10;
[multiplied, divided, added, subtracted, someExtraVar] = testFunction(structa,num);
structa.multiplied = multiplied;
structa.divided = divided;
structa.added = added;
structa.subtracted = subtracted;
structa
function [multiplied, divided, added, subtracted, someExtraVar] = testFunction(structa,num)
multiplied=structa.mat1.*structa.mat2;
divided=structa.mat3./structa.mat4;
added=structa.mat5+structa.mat6;
subtracted=structa.mat6-structa.mat6;
someExtraVar=length(structa.mat6)*num;
end
Whether Style 1 (expansive) or Style 2 (compact) is preferable depends on user preferences and the requirements of the project. Is testFunction() a standalone function, or are its outputs intended to be updated or reused in a parent function or a main script containing loops?
Style 1 generally requires users to remember the order of the output arguments. For one-time execution involving only a few outputs, Style 1 may be more convenient for most users. Even so, I often confuse the azimuth and elevation angles when using [out1, out2] = view() to readjust the camera’s line of sight with view(out1 + value1, out2 + value2).
When there are many computed outputs, Style 2 is usually more practical. For example, simOut = sim(model) contains all the data logged during the simulation, as well as metadata describing the simulation. However, you can write a wrapper function based on Style 1 to determine how the data should be handled before making changes to the structure.
You might also want to combine the advantages of both styles by using nargout, which returns the number of output arguments requested by the caller of the currently executing function.
sys = tf([1], [4 3 2 1])
% Style 1
[out1, out2, out3, out4] = margin(sys) % [Gm, Pm, Wcg, Wcp] = margin(sys)
% Style 2
structA = stepinfo(sys)
"Is there an accepted best practice for this/does anyone have any opinions or suggestions?"
There is no single "best" approach to this. Like everything to do with code, "it depends" is the the correct answer.
"since the second option is modifying globals locally within function"
Do not let some abstract rule-of-thumb override your concrete needs and requirements. A rule of thumb is only a rule of thumb, if you can justify why your task is better suited for some other design then use that other design.
Remember that correctness is always your first priority: if this is easier to justify with option 2, then that overrides some abstract rule of thumb that might provide 3ns runtime improvement vs. failing to ensure correctness which costs you hours/days/weeks of your actual progress instead.
Personally I would favor simplicity in your situation. Often when you try something you find that several other parts of your design simplify and refactor into neater code too, which is usually a good indications that you have found a good abstraction of the task at hand. So, try option 2 and see what happens.
Oshani
on 10 Sep 2026 at 15:05
"'... I'm passing a structure to a function, adding some fields to the structure, and returning the structure..."
Your sample code doesn't show the use of the function to judge, but I'm not at all in favor of the above logic/function behavior. I would strongly recommend that the struct should have its fields defined in the calling routine and the function only populate those with the proper results.
This would then have the syntax looking like Option 2, but the fields would be known a priori in the caller; they could be hardcoded as here (in which case there's no reason at all not to do so) or created dynamically if the user is to be granted the ability to define some specific statistic. But, if that were to be the case, there would have to be a way for the user to select or define what that other statistic should be and there doesn't appear to be anything of that nature being provided.
As a general stylistic note, I find the very long variable names extremely distracting and making for very difficult code to read and comprehend; this is particularly true when they all begin with a long string that is the same so it is only the ending that is different.
One minor efficiency, if you have the Statistics TB, then could do the z-statistic along with mean, std as
[muFS,sigmaFS] = meanAndSigma(filteredSignal);
if params.normalizeFS==1
normFunc = @(x) (x-muFS)/sigmaFS ;
filteredSignal=normFunc(filteredSignalAll);
filteredAtEvents=normFunc(filteredAtEvents);
[muFSNew,sigmaFSNew] = meanAndSigma(filteredSignal);
else
muFSNew=muFS;
sigmaFSNew=sigmaFS;
end
is
[filteredSignal,muFS,sigmaFS]=zscore(filteredSignalAll);
filteredAtEvents=zscore(filteredAtEvents);
[muFSNew,sigmaFSNew] = [0 1];
because you have to return the z-score statistic anyway or else it is undefined in the struct and unless you've got unshown initialization code in the production code not in the sample code, it will error with Option 1 on having undefined/uncalculated outputs.
As @Stephen23 points out, "simplify, simplify!". Something that was one of Einstein's notable maxims can't be wrong advice. :>)
One minor efficiency, if you have the Statistics TB, then could do the z-statistic
x = randn(1, 1e6);
[xnorm, c, s] = normalize(x, 'zscore');
format longg
[c, s; mean(x), std(x); c-mean(x), s-std(x)]
isequal((x-c)/s, xnorm)
How does this compare with zscore?
[filteredSignal,muFS,sigmaFS]=zscore(x);
isequal(filteredSignal, xnorm)
isequal(muFS, c)
isequal(sigmaFS, s)
Accepted Answer
More Answers (1)
Which of the following is considered best practice?
I would say the most common (and therefore perhaps the best) practice is Option 2. It's essentially what you're always doing in object-oriented programming. There is little difference between struct field modification in a function and object property modification inside a class method.
Option 1 seems better to me, since the second option is modifying globals locally within function. The first option also tells you exactly what outputs the function returns without having to actually read the function.
As a preliminary remark, I don't know what "modifying globals" means in this context. The fields of a struct do not behave like global variables in any way that is intuitive to me.
Generally speaking though, the very purpose of a function is to hide the details of what it is doing from the calling routine, and the purpose of a struct is to hide the variables that it carries. You want to do that to reduce code clutter, and Option 1 works against that. If you need visual reminders of what a function call is doing, that is normally accomplished using code comments and help documentation.
Further visuals cues can come from choosing variable and function names that are suggestive of what the variable contains and what a function call is doing. In particular, reusing the name structa for the output of,
[structa, someExtraVar] = testFunction(structa, num);
is awkward, since the output structa has new fields and therefore substantially different composition from the input. Instead, I might do,
[augmentedStruct, someExtraVar] = augmentMatStruct( structa, num);
Categories
Find more on Programming Utilities in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!