This isn’t specific to Julia but applies to all languages that have object-orientation or user-defined data structures. There are two typical uses for structs/objects: (1) abstraction, and (2) encapsulation.
Abstraction is for when you have a domain-specific “object” that you want to reason about or operate on. Since you probably have a math background, consider that mathematics behaves similarly. You might say that numbers are the “built-in” data types of mathematics. Not using structs in Julia is the equivalent of doing “just algebra” in math. But then you start defining abstractions once you move to a more specific domain, e.g., geometry. You then ask questions like “At what point do two lines cross?” after you’ve defined “line” and “point” as something you want to reason about. As such, the
Point struct in the documentation is not just a trivial example. If you’re writing geometry-related code, you actually want to define such structs to simplify your code.
Another aspect of abstractions is that they are often the key to generalization: consider that you could define the abstract behavior of points (via methods) which would then still apply if you wanted to formulate a
PointSphericalCoordinates struct later on.
The second reason to use structs is for the encapsulation and management of state. Consider what happens internally when you use
DifferentialEquations to solve the ODE that you set up with
integrator = init(prob, alg; kwargs...)
prob here again is an example of “abstraction”, but the
integrator is an object that holds the state of the solver between steps (the state vector, the current time, etc.)
Another example for “state management” is the situation where you find your function has 20 input parameters (which are then maybe passed to another function). To simplify the code, you can wrap all of these parameters into a struct to be able to pass it around more easily.
State management is a much trickier use of structs than abstraction, simply because management of state is basically the problem of programming (even more so once you get to questions of concurrency). Managing state is hard, and virtually all non-trivial bugs stem from the mismanagement of state, which is probably why the example in the documentation deals with the easier “abstraction” use case.
An important part of encapsulation is that it can hide complexity by having private fields. For example, the
integrator only documents “useful” fields. This is especially true in Julia, where it is quite common for the fields of structs to be considered completely private, maybe accessible as properties or more commonly via accessor methods. In many cases, interfaces are defined purely in terms of methods.
Generally, “abstraction” structs should be immutable, while “state management” structs are mutable (or at least have mutable components), so Julia actually makes the distinction somewhat more clear than other languages by having the