As argued by @phipsgabler you are mixing code from different versions of Julia.
On this specific topic you can look also on my "Julia concise tutorial", on the topic of "Custom Structures".
The same topic, extended, is available in the "Julia Quick Syntax reference" book (Apress, 2019) from which I report an except from the unedited source (still full of English errors, but you should get the point..):
Custom Types
In Chapter 2 "Data Types and Structures" we discussed built-in types, including containers. In this chapter we move to explain how to create user-defined types.
"type" vs "structure":
Let's be clear about these two terms and what they mean in the Julia language context.
As type of an object we mean, as in plain English, the set of characteristics that an object can be described with. For example an object of type sheet can be described with its dimensions, weight, colour..
All values in Julia are true objects belonging to a given type (they are individual "instances" of the given type).
Julia types include so called primitive types made of just a fixed amount of bits (like all numerical types.. Int64, Float64, but also Char..) and composite types or structures, where the set of characteristics of the object is described trough multiple fields and a variable number of bits.
Both structures and primitive-type can be user-defined and are hierarchically organised. Structures roughly correspond to what classes are known in other languages.
Primitive type definition
A user-defined primitive type is defined with the keyword primitive type and just its name and the number of bits it requires:
primitive type [name] [bits] end
For example:
primitive type My10KBBuffer 81920 end
IMPORTANT: A (current) limitation of Julia is that the number of bits must be a multiple of 8 below 8388608.
Optionally a parent type can be specified:
primitive type [name] <: [supertype] [bits] end
Note that the internal representation of two user-defined types with the same number of bits is exactly the same. The only thing that would change is their names, but that's an important difference: it's the way that functions are defined to act when given objects of these types are passed as arguments that changes, i.e. it's the usage across the program of named types that distinguish them rather than their implementation.
Structure definition
In a similar way of primitive types, to define a structure we use the keyword mutable struct, give the structure a name, specify the fields, and close the definition with the end keyword:
mutable struct MyOwnType
field1
field2::String
field3::Int64
end
Note that while you can optionally define each individual field to be of a given type (e.g. field3::Int64) you can't define fields to be subtypes of a given type (e.g. field3<:Number). In order to do that you can however use templates in the structure definition:
mutable struct MyOwnType{T<:Number}
field1
field2::String
field3::T
end
Using templates, the definition of the structure is dynamically created the first time an object whose field3 is of type T is constructed.
The type with whom you annotate the individual fields can be either a primitive one (like in the example above) or a reference to an other structure (see later for an example). +
Note also that, differently from other high-level languages (e.g Python), you can't add or remove fields from a structure after you firstly defined it. If you need this functionality use <> instead, but be aware that you will trade-off this flexibility with worse performances.
At the opposite, to gain performances (but again trading up with flexibility), you can omit the mutable keyword in front of struct.
This will constraint that once an object of that type has been created, its fields can no longer be changed (i.e., structures are immutable by default).
Note that mutable objects -as arrays- remain themselves mutable also in a immutable structure.