I would say that the first question to ask is "Why put such functions inside either a record or a class?" in the first place.
The use of record methods as in IOUtils is in many cases largely arbitrary in Delphi and in part reflects a fetish with following conventions in other languages that are not directly relevant in Delphi.
In some languages (Java and .NET?) "first class" functions are not supported. That is, procedures and functions cannot exist independently of any container class. Hence in those languages if you have functions and procedures they must be contained in a class. If they do not operate on member data then obviously they are declared as class methods to avoid having to construct an instance merely to call what is in fact a classLESS function.
i.e.
TDirectory.Exists(s) and TFile.Exists(s) are syntactic alternatives to DirectoryExists(s) and FileExists(s).
In Delphi first class functions are supported and there is therefore no real reason to use classes or records to discriminate between functions, except as a way of facilitating ease of reference for a developer when writing code and using IDE assistance to browse for available methods.
Without a container record/class you have to know that there is a function called "DirectoryExists()". With a container record, you only have to know that there is a type TDirectory, and the IDE (via code completion suggestions) can then present all of the "methods" available for TDirectory operations.
However, in Delphi there is an alternative made possible by the support for first class functions. Rather than using arbitrary and artificial "containers", you (or rather the authors of IOUtils) could instead have chosen to use the existing container - a Delphi "unit":
unit IOUtils.Directory;
interface
function Exists(s): Boolean;
and
unit IOUtils.File;
interface
function Exists(s): Boolean;
This still enjoys the benefits of IDE support in the form of code completion suggestions and also provides the namespace separation required to support identical function names operating on different things.
The downside of this approach (and it is a big one) is that where a unit uses both IOUtils.File and IOUtils.Directory, it must qualify one or other function name to avoid confusion, but this qualification could be omitted, which is why it is so dangerous:
uses
IOUtils.File,
IOUtils.Directory;
..
begin
if Exists(s) then // << tests existence of a directory
..
if IOUtils.File.Exists(s) then // << ensure we test for a file
..
if IOUtils.Directory.Exists(s) then // << ensure we test for a directory
end
This may also be an advantage of course, if your unit contains code that ONLY works with directories, it will use only IOUtils.Directory and need not qualify and repeat that fact anywhere other than in the uses clause.
A potential and very REAL danger here of course is that in future if the unit is extended and subsequently starts using IOUtils.File, then this could easily lead to scoping errors on any unqualified references.
Which approach you prefer in your code will depend largely on personal preference and the possibility for collisions in scoping errors where you have similar/identically named or semantically equivalent functions operating on different entities.
For those reasons, the design choice in IOUtils at least is understandable and arguably a good one, tho ymmv.
But in other cases, if all you are seeking is a way to group related functions that are not at risk of confusion/collision with similar functions, then the use of an artificial and arbitrary container is superfluous. The unit that the functions must reside in may itself provide all the grouping behaviour you need.
As for why a record rather than a class...
I suspect that there may be a small static memory benefit to using a record rather than a class when fabricating these arbitrary containers. I imagine that a class has additional overhead, tho this is going to be relatively small and static in nature (i.e. a bit more overhead for each class, but not related to the number of times it's class methods are used).