- Right time for an example of all this,
is in the usual project, lesson six, scope ownership.
And we are going to look at this file here,
demo static local.
I'm going to run the application several times,
with different code tweaks to illustrate particular points.
So let's go into the code and have a look.
Right here we are.
So it's demo static local.
I've got quite a few things I want to explain here.
I'm going to be doing some sleeping in my code.
I'm going to be doing some some code.
I'll sleep for a few seconds
and then I'll run some more code.
So I'm going to be importing
I'm going to be importing the lazy structure
which is defined in the sync module in a crate,
which is like an additional library, called once cell.
When you import, when you use libraries in Rust,
most of the libraries, most of the functions
and things that you're going to use,
are in the standard crate.
So for example the standard crate has a module
called thread, which has a sleep function.
And the standard crate has a module called time,
which has a duration structure.
Basically things that start with a capital are types,
like structures or enums or traits,
and things that start with the lowercase
are usually functions.
So the duration structure
and the sleep function are in the standard crate.
They're automatically available in the application.
But the once cell crate, we have to actually add
as a dependency to download into our application.
And I've done that in the toml file.
This is the first time we've actually seen
an additional crate needed to be specified as a dependency.
So once cell is a crate that I need to download
into my application,
chrono is another one, another dependency.
So my dependencies, I pull in the current version
of the one cell crate.
A crate is like a library.
And I also pull in the chrono library as well.
The star is basically the current version.
There are other versions I could specify
and it'll just download the current version
into my local directory structure.
It'll be downloaded into my current project directory.
Okay, so let's go back into the code then.
So lazy is a structure in the sync module
in the one cell crate, date, time and UTC.
Both of those are structures defined in the chrono crate
and in my demo I've got two different parts to the demo.
I've got a call, a function which illustrates how
to initialize statics at compile time.
Let's have a look at that function first.
Simple enough.
I've got a static string, effectively a string,
slice technically, but we'll just call it a string
for now.
And I've given it a compiled time constant.
How is your Welsh today?
Croeso o Gymru, welcome from Wales.
And I print the message.
So when you have a compile time value
to assign to a string that will actually be initialized
at compile time, it will initialize that static variable.
And it's not multiple.
So it's fixed to be Croeso o Gymru.
So that's the first function call.
The second function call is much more interesting.
So let's have a look at this.
Let me talk you through this function.
This is where I want to initialize the static at runtime,
when the program is actually executing.
So this first statement won't work.
You can't initialize a static directly at runtime
because it's not thread safe.
If you try to, you'll get a compile error.
Let's try to initialize a static with a runtime value
with a function called at runtime,
call that function and assign the result to a static.
Won't let me do it because it's not thread safe.
Okay, well let's prove the point that that's not gonna work.
I'm gonna try to do a cargo check.
Okay, just to check my code.
Right, cannot, oh, what's this saying here.
It's basically saying here, it cannot call this function.
I can't call that function, because it's a runtime function.
It's not a constant value,
it's something that has to be evaluated at runtime.
So the error message is starting to get
a little bit technical.
Oh, consider wrapping this expression in a lazy wrapper.
Well, funny you should say that.
That's exactly what I've done.
So let me get rid of that error message.
And let's see.
Now what I've done here, is I've fixed the error message
and then I'll explain the code and then we'll run it.
I've got an element of sleep involved here to actually show
the time changing basically.
So initially I print the current time,
the UTC structure has a now function.
It returns the current date and time
in universal coordinator time.
So basically Greenwich meantime.
And when you have a UTC object,
you can call the format function.
It basically contains the date and the time.
You can tell it to format a string containing just the time.
Okay?
So if you want details of all the formatting,
I won't go into it here.
Have a look at the UTC structure.
Have a look at the format function
and it'll tell you what different parameters
you can pass in here to output the date time as you want.
This will output the current time percent T for time, okay?
So display the time when the function first enters.
And then I've got a static variable
which is going to be initialized using lazy initialization.
It will effectively be a date, time,
universal coordinated time,
but it'll have to be initialized lazily.
And I give it a lazy object that will create a lazy object
with a closure, an anonymous function if you like,
or a lambda.
That lambda will be invoked the first time
I try to access that variable.
Okay, and only at that point.
So down here I try to access the variable timestamp,
is a lazy object.
The contents of that lazy object will be a date time.
Okay, so I tried to dereference the lazy object
I tried to get the contents of the value inside it
and at that point it will trigger the first time
I try to access the content of the lazy
it'll call the lambda at that point in time.
And at that point in time, I sleep.
Sleep remember was the function that I imported
from the thread module.
That's the way that you sleep the current thread.
So I sleep the current thread,
and the sleep function takes an a duration object,
which makes sense.
I've created a duration object here,
I specified I want to wait for five seconds
and zero nanoseconds.
The duration new function,
you give it a seconds as the first parameter,
and nanoseconds, additional nanoseconds
as the second parameter.
So the current thread will sleep for five seconds.
Remember this closure is being invoked kind of here.
So at that moment it'll sleep for five seconds.
Get the time after that five seconds, okay?
And display it.
It'll display the current time after five seconds
and whatever you return from this closure,
this value that you return is the value
that this then will be.
So if you follow the logic, when my application starts
the current time will be now.
Five seconds will elapse
before I evaluate the value for my timestamp.
So the timestamp will effectively be five seconds later
than the initial time.
So the initial time will be x,
the timestamp will be x plus five seconds.
Okay, so hopefully that makes sense.
Let's actually run the application from main.
So in my main code I'm going to invoke
demo static local do it.
Let's uncomment that statement
and let's run the application.
I'm gonna go back into the code here
just so we can kind of see what's going to happen.
Cargo run.
Okay, so there's the five seconds wait
and that's the output.
So in demo static local, that was just here
it output the simple Croeso o Gymru, welcome from Wales
you are very welcome.
And then it came into the initialization at runtime,
which is all together more interesting.
It output the current time
at the beginning of that function, which was 11:51:45, okay?
Then I tried to access the timestamp
which will trigger this call,
this code, to be executed just in time.
Sleeps for five seconds.
So the time here will be five seconds later.
That's the current time, five seconds later.
Okay, so that's 11:51:50, five seconds later than it was
that's the time that I assigned to my timestamp.
And that's the value of the timestamp here.
Okay, five seconds greater than that.
So an interesting thing is that this static
only gets initialized the first time that it's used.
If I didn't use this timestamp,
imagine I didn't actually use the timestamp,
I'm just gonna comment that line out.
Then what do you think would happen now?
Gonna run it and see if you can think
what's gonna happen now.
Well look at that.
Is that what you expected?
It came into here and it said the current time is 11:53:17.
It recognized this lazy constructor,
but it hasn't called this lambda
until we use timestamp and because we didn't use timestamp,
it is lazy.
It only evaluates the closure at the first time you need it.
And if you never actually use it
then it never bothers running the code.
Why bother running the code
if you're never gonna use the value?
It is actually lazy,
and the other thing to note is that it'll only run that code
the first time you try to access it.
So what would it happen if I did this?
Let's say I basically call that function,
that code twice and I have a little sleep in here.
I'm gonna copy this code,
so I don't have to repeat myself.
So what do you think is gonna happen now?
Let's just run it
and give you a chance to think of what's gonna happen now.
And waiting patiently,
waiting very, very patiently.
Right, let's go through what happened then.
So current time initially was 11:54:25.
I declared my static and the compiler
or at runtime it said, "Right, okay, I'll bear that in mind.
I'll bear in mind that's the code
but I won't run it until the first access."
Well, here's the first access.
So at that point it did have to run the code,
when I call on line 36.
At that point it did have to access to run the lambda.
So it ran the lambda at that point, waited for five seconds,
output the current time after five seconds.
And then that value is the value of my timestamp variable.
Okay, so there's the five second wait
and then the timestamp variable here is five seconds later
than the variable initially up here.
I sleep for five seconds, okay.
And then I access the timestamp.
The timestamp available is only initialized once, okay?
So it doesn't repeat the code.
It already has an initial value
when it ran the the lambda the first time.
So for the second time the value is the same as before.
Okay, so it only initialized the value,
it only ran the closure,
the first time we accessed variable here.
The second time we access it,
it already had available.
Okay, so it is quite involved, much harder work
than you'd been used to from C and C++.
The whole point is that even though it's harder work,
it is thread safe.
Okay, so ultimately that's the most important thing.