[No Audio]
A structure creates a data type that can be
used to group items of possibly different
types into a single type. A structure is used
to represent information about something more
complicated than a single number, character,
or Boolean, and even more complicated than an
array or vector of the above data types. For
example, a student can be defined by his or
her name, GPA, age, user Id, etc. Each
of these pieces of information should be
labeled with an easily understood descriptive
title, and then combined to form a whole which
is referred to as what we call a structure.
Pretty much exciting material is coming our
way, so let's dive into it.
The very first thing about structure is, how to define or
declare a structure? For declaration, the
reserved word of struct will be used followed
by the name of the struct. Please note that,
in Rust, the convention for the names of the
struct is that of camel case. There are no
spaces between the individual words and the
first letter of each word, of the name starts
with a capital letter followed by lowercase
characters. Let us define a struct called
Person, which will be used to store the
general information about a person such as
his citizenship information, name, age,
gender and salary. Inside the body of the
structure, we will declare the names of the
fields of the structure along with their types.
The fields can be of different types,
and we can have as many fields as we may like.
[No Audio]
The structure will be typically defined
outside the body of the main function. Now,
let us use the structure inside our main
function. To declare an instance of this
structure or an element of type Person, we
will use the lat, and then name of the
variable which we want to have for instance person1.
[No Audio]
This means now that the variable person1
is of type Person, which is a structure.
Next we will define values for the
individual fields, by writing values for all
the fields one by one. I will use my Person
information in this case.
[No Audio]
Please note that since this is an assignment
to a variable, therefore, the body of the
code segment should end with a semicolon.
Moreover, it is not necessary to match the
order in which the fields are defined inside
the structure definition. Okay, great so far,
to access the individual fields of the
structure, we will use the name of the
structure variable followed by a dot and then
the name of the field. Let us display the
values of the of some of the fields of person1.
[No Audio]
Let us cargo run to check.
[No Audio]
Now once the data is being organized in
the form of structures, we can define
functions which operates on the instances of
specific structures. To better organize the
functions, that we may define in the context
of a specific structure. The Rust
provides an implementation block for the structures.
Let us define compute_taxes function
for the Person structure,
I will do it inside an implementation block. To
have an implementation block for a
structure, we will use the impl keyword
followed by the name of the structure.
[No Audio]
This is called an implementation of the structure,
which is nothing but the code block which
contains the relevant functions that we can
have for the respective structure. Please
note that, we do not frequently use the word
of variable when referring to an instance of
the structure, but rather we call it as an
object or an instance of the structure.
[No Audio]
Let us have a compute_taxes function now.
[No Audio]
This function will take a reference to
the structure instance which is calling it,
and then it will return an f32 value, which
will be the value of the taxes that is
applicable on the person. To mention at the
reference of a structure object or instance,
which is calling this function, I will write &self.
This basically means
a reference to the calling structure instance,
for which we can call this function.
Inside the body of the function, I will write some
formula, such as self.salary as f32/ 3 times 0.5.
[No Audio]
And I will not include
any semicolon to indicate that this
is a returning value. Please note that, the
self followed by a dot, can be used to refer
to the individual fields of the structure, for
which we are calling this function. Let us
call this function from the main.
I will call it inside a print statement by calling it
for the instance of person1.
[No Audio]
In this case, now the person1 will be
passed as a reference to the function, and in
the &self inside the function means a
reference to person1 . Please note that,
under the fn compute_taxes
requires an explicit input, however, with the
.syntax, the difference is passed by
default, and we do not need to mention it explicitly.
Let us note that, the function is
being called for person1, and therefore,
we do not need to mention it again, let us cargo run this.
[No Audio]
We may note that, the taxes value
has been computed correctly. Okay please
note that, in order to access the compute_taxes,
we are required to use the dot notation,
it let the compiler knows which
particular function I am referring to. In case
I do not use the dot notation, then in that
case I will write something like person::compute_taxes.
The double colon syntax kind of tells the Rust compiler
where to look for the function. In this case
now, we will be required to explicitly
mention the inputs to the function, because
the compiler is unable to know for which
instance of the person you want to call this
function. So, I will write something like
&person1, which will pass a reference to the
function for person1. This will have
exactly the same effect as person1.compute_taxes.
Let us execute to confirm this.
[No Audio]
I will however, prefer the style of writing it
in the form of person1.compute_taxes.
In summary, with the double
colon, the function is available to the type
itself, that is the type Person, which we
mentioned before the double colon. With the dot,
we perform some operation or functions such as
reading, writing, processing, or updating the
data of the specific type of instance.
The double colon has another advantage which
will become evident to us, once we cover the
module system of Rust. Sometimes, it is very
useful to have an initializing function to
initialize the value of a new structure
instance from some default values. This helps
in reducing some codebook and quick
initialization of new structure instances.
For instance, if I want to declare 10 new
instances, 10 new structure instances, then
I will have to write the code something like
the code above 10 times. Instead, I would
like to deduce the code, and we'll
initialize the structure fields from some
useful defaults, which I may later on update.
The initialization function is typically
named as a new function. You may
name it as you want such as initializing
function, or whatever, the convention however
is to call it new. This function will also be
added to the implementation block of the
structure. Let me add this.
[No Audio]
This function will return a structure instance.
So, instead of having a reference
which is specified above
by mentioning &self, I will mention the
structure that I create here by writing Self
with a capital S. The Self with a capital S
inside the implementation block refers to the
structure type itself, which is the Person itself.
This means that this function will
return a structure of type person, since it is
defined inside the Person structure
implementation block. Next, I will add the
body of the function. I will define the
structure of a Person with some default
values for its fields.
[No Audio]
We may now call this function from the main
to create new instances of the type Person.
Let us create one with the name of person2
and we'll assign it to the returning value
from the new function.
[No Audio]
Let us now check if the function has
successfully created a new instance by
displaying some of the fields.
[No Audio]
Let us execute now.
[No Audio]
person2 has been successfully initialized from
proper default values. Please note that, inside the new
function we may write instead of Self Person,
because we have the same meaning within this context.
Similarly, for the person inside the
body of the function we may also write Self
instead of Person, because Person and Self
have the same meaning inside the
implementation block. In general, inside the
implementation block of a structure the Self
is referring to the structure type itself.
The Rust provides another useful way for
quickly initializing a structure from the
values of some other already existing structures.
Suppose we want to have another
instance of the structure such as person3,
and I want to just update its age and
name, and the remaining fields should be the
same as that of person1. So I will just
update and mentioned the values for the age and name.
[No Audio]
And we'll have a couple of dots, and
after that the name of the structure instance
person1 from a where the remaining values
will be initialized from. Let us print
some of the fields of person3 and
execute to see the values.
[No Audio]
All these structure instances that we have
created so far were immutable. This means
that their values cannot be changed once
assigned to them just like immutable variables.
Let us create a new immutable
structure of person4 and display some of its values.
[No Audio]
Let us see if we can now update some of its
values, I will update the name and print the value.
[No Audio]
Let us execute.
[No Audio]
We may note that, the field value has been
updated from the default value. Okay now, let
us move to another topic called tuple structure.
[No Audio]
A Tuple Struct is very similar to that of a tuple,
but with a key difference, that we have
a name of the tuple also. Simple tuples do
not have an associated name, while the tuple
structs have an associated name. Unlike
typical structures, the tuple structs will
not have a names associated with its
individual fields, and therefore, they are
referred to as tuples structures since tuples
also have no names for its individual fields.
Things will become more understandable as we
look at an example. Let us create a tuple
struct called Numbers containing a couple of i32 values.
[No Audio]
I can now use it inside the main function.
Let us define an instance of this
with the suitable name.
[No Audio]
Please note that the
fields do not have any names, and we may
therefore access them using their indexes.
Let us add a print statement for displaying
the values of the fields.
[No Audio]
Let us execute to see the result.
[No Audio]
Let us implement some simple functions on
this tuple struct. Remember we need to have
an implementation block first for this purpose.
[No Audio]
I will now add some functions to the
implementation block. I will include a greater
than function which will determine the
greater of the two fields
[No Audio]
Since I am not modifying the values of the
structure, so therefore, I will use a
reference to the structure. Moreover, we use
a dot notation to access the individual
fields of the structure. Let us include one
more simple, a lesser than function which will
have a very similar structure and definition.
[No Audio]
Let us call these functions from the main
function now. I will call it inside a print statement.
[No Audio]
Let us cargo run.
[No Audio]
The result is as expected. With this we end
this tutorial, we have looked at some of the
preliminary details of structure. In the
upcoming tutorial, we will be covering some
advanced topics on structures. So do come
back for covering that, and until then,
happy Rust programming.
[No Audio]