- We've already seen how to define static local variables.
As you can see in this example,
I've got a function, and inside the function,
I've declared a static variable called MESSAGE.
It's a string.
Technically speaking, it's a string slice.
We'll need to discuss that syntax later on.
And I've given it a value, in this simple example,
I've given it a hard coded constant string, "Hello."
Right, so when you declare a local static variable,
it's only visible inside that function.
And it retains its values across calls.
So, it's local, and it is statically allocated.
Initialized on first entry,
and it retains its value thereafter.
And of course, like I was saying,
you can declare it as multiple, if you want,
if you need to change it,
you have to be a bit careful about that, with thread safety.
But it is possible.
Right, so that's for local statics.
It's also possible to have statics declared globally.
Global static variables,
which can be accessed by all functions,
potentially in the current file and other files as well.
So, the rules for declaring a static global variable
are the same as for static locals.
You have the static keyword
and you specify the variable name in capital letters.
You specify the data type,
you have to specify the data type,
it doesn't allow type inference.
And you supply a value.
Typically a constant, although possibly a runtime value.
So, there is a, well, there's several differences,
but one key difference
compared to other languages like C++.
When you declare a global in C++,
you just declare it at the top of a file.
But in Rust, I mean, you would declare it
at the top of a file, but you also have to say static, okay?
And you don't have to do that in C++,
but you do have in Rust.
So, here's an example.
I've got a static global variable,
presumably called MESSAGE, it's a string, a string slice.
And I've given it the simple value, "Hello."
And I could then potentially access that message anywhere
in my file, or anywhere in my project if it's public.
So, let's talk about visibility.
If you want to make a global variable visible
to other modules, other files,
then you declare it as public like this.
So if I've got a module called module1.rs, I declared it
as a public static, I can then use it in another module.
Okay?
So, in order to be able to access it in another module,
you'd have to, first of all, in your main code,
you'd have to introduce module1.
And then, you'd potentially import the variable name
into the current scope.
Okay?
So, it could look something like this.
And import
the code.
And basically declare the sub module.
And then, the use statement from the module1,
introduce this symbol into the current scope.
Okay?
So, declare a sub module,
and then make this symbol name directly visible,
so I can then use it here.
MESSAGE.
If I hadn't imported the variable like this,
then here, I'd have had to have prefixed it
with a module name, something like this.
module1:: MESSAGE.
Okay? Which isn't the hardest thing in the world.
But it might get a bit repetitive.
So, often, you just use the use statement
to basically input that name message
into the current scope.
Right!
If you want to initialize a global variable lazily
at runtime, similar to what we looked at
in the previous demo, actually, you use the Lazy wrapper.
So, here, I've got a static,
I've called it GLOBAL_TIMESTAMP.
Whatever name you wanna choose.
And I want to initialize it with a runtime expression.
You can't just initialize it directly
because of thread safety.
You wrap it in a Lazy box.
And the first time you try to use this variable anywhere,
then the code that you supply here
in the new function, this closure,
this green closure, will be executed on first usage.
The value that this closure returns will be the value
that gets placed inside the Lazy wrapper.
So, the Lazy object is a thread-safe wrapper
around the value.
The initialization code in my closure, it runs once
the first time you try to use this variable.
Okay?
So, basically the same as what we had for static locals,
but at a global scope.
So, let's see an example
in the lesson six scope ownership project.
Let's have a look at demo_static_global.rs,
and then we'll run it.
So, here we are in the project.
We're going to look at demo_static_global.
And let me highlight
a few things in here.
Right!
So,
remember, that I've specified once_cell?
That crate?
And chrono?
That crate are both being introduced into my project.
And the once_cell crate defines the Lazy structure,
the wrapper that allows Lazy thread safe initialization,
and the chrono crate defines a couple of date
and time-related structures.
I'm going to be calling the sleep function
for dramatic effect,
and I'm going to use the duration structure
to specify the duration of my sleeps.
Okay, I've got a public
global static called GLOBAL_MESSAGE.
That's a simple string or string slice,
I should say, initialized with some hard-coded text,
that's quite easy,
so this variable will be initialized
by the compiler at compiler time.
And because it's public, as well as being able to access it
in this file,
I could potentially access it elsewhere as well,
so bear that in mind.
And then, I've got another static, this one isn't public.
So, I decided not to make it public.
(keyboard keys clicking)
So, it can only be accessed, it's kind of global,
but only global to this file.
Okay, so get access GLOBAL_MESSAGE anywhere
across the entire application because it's public.
But GLOBAL_TIMESTAMP, I can only access
in the current module, because it's not public.
And it's also going to be initialized at runtime,
so we have to use this Lazy mechanism again.
Use the Lazy wrapper, it'll eventually contain a DateTime
for universal coordinated time.
To initialize it,
I provide a closure, same kind of thing as we saw before.
This closure will be executed once,
the first time we try to access this variable anywhere,
this closure will be executed,
and once only.
And get the current time at runtime.
A quick print line message to say
that the global variable called GLOBAL_TIMESTAMP.
And this is the current time, okay?
Now is the current time after that.
So, whatever the current time is, will be displayed here.
And so, this print statement just shows
that it has been initialized,
and we'll only see that appearing once.
No matter how many times we use this variable,
this closure only gets executed the first time.
So, only the first time we use this
will we see this message appear.
So, the time that or the DateTime that gets returned,
that will be the value for my GLOBAL_TIMESTAMP.
Right, so anyway, the entry point, if you like,
for this demo is here.
And I call function 1 a couple of times.
And I call function 2 a couple of times.
So, let's have a look at function 1.
So, function 1 accesses the GLOBAL_MESSAGE, which is here.
This is a GLOBAL_MESSAGE.
And it also accesses the GLOBAL_TIMESTAMP.
Remember, GLOBAL_TIMESTAMP was initialized dynamically,
it's a Lazy object.
We use the star to de-reference, to look inside
the Lazy wrapper, to gimme the contents.
A bit like C++ pointers being de-referenced.
Give me the GLOBAL_TIMESTAMP.
So if this is the first usage of GLOBAL_TIMESTAMP
at this moment, it'll have to be evaluated.
Okay, and this closure here will be executed
to give me the value of the GLOBAL_TIMESTAMP.
So, the first time I call f1,
that will call the initialization block to run.
To initialize for the first time.
The second time I call f1, it's already been evaluated.
So, the GLOBAL_MESSAGE will be the same.
The GLOBAL_TIMESTAMP has already been evaluated.
So, it won't call the lambda a second time
because it has already been evaluated.
That's why in my demo,
I've called the function twice to show that
on the first call, this Lazy initialization will occur.
On the second call, the initialization's already happened,
so just reuse the value at that point.
And then f2, f2, similar kinda thing.
And f2, I wanted to highlight the fact
that this really is a global variable.
You can access it in any function,
I can access GLOBAL_MESSAGE and GLOBAL_TIMESTAMP
in that function, and I can access GLOBAL_MESSAGE
and GLOBAL_TIMESTAMP in this function as well,
because it's a global!
Right.
And furthermore, because GLOBAL_MESSAGE is public,
I could access GLOBAL_MESSAGE in other modules as well.
Like in my root crate, if you go back to the main.rs.
I'm gonna basically access GLOBAL_MESSAGE
in my other module as well, in main.
So, what I've done is, I've introduced it into scope.
I said, basically, from demo_static_global,
introduced the GLOBAL_MESSAGE into scope.
And then down here,
I access GLOBAL_MESSAGE.
If I didn't have the use statement,
and it wouldn't be the end of the world,
it just means I would then have to explicitly qualify
the symbol like that.
Okay, which is also allowable,
but starting to get a little bit verbose, really.
So, let's just revert to the import statement here.
Introduce into the current scope, that symbol.
So, I can just access it locally.
And because GLOBAL_MESSAGE was public,
I'm allowed to do that.
If GLOBAL_MESSAGE hadn't been public,
then no matter how hard you try,
you can't access it elsewhere,
it really would just be private to this file in that case,
so let's make it public again.
Let's go back into main.rs.
And let's invoke our demo, demo_static_global.
(keyboard keys clicking)
And come at that statement.
And I'm gonna run the code.
(keyboard keys clicking)
I'll go run!
So, let's have a look!
Let's just trace our progress.
So, in demo_static_global.
And ooh!
Actually,
this is
interesting.
It has performed the initialization of my GLOBAL_TIMESTAMP.
I'm gonna just run this again in case I've missed something.
Yes, there we go, that's better.
So, it comes in here, and it displays
In demo_static_global::do_it().
Okay, so that's good.
And then, we call f1.
And f1.
It prints the GLOBAL_MESSAGE.
So, there's the GLOBAL_MESSAGE.
This is a GLOBAL_MESSAGE.
And then, it tries to access GLOBAL_TIMESTAMP.
And because this is the first time we've tried to access it,
Lazy initialization kicks in.
So, at that point, it will invoke the lambda or the closure.
And it'll say the GLOBAL_TIMESTAMP is being initialized now.
So, that's the message here.
That's only gonna appear the first time.
Whatever it returns, that is the value of GLOBAL_TIMESTAMP.
Okay, so we can forget that now.
And it'll print the value for GLOBAL_TIMESTAMP.
And that is the value for GLOBAL_TIMESTAMP.
Okay, good.
And then, we call f1 again.
And this time, it'll just access the variables directly,
without any need to run the closure again.
So, in f1 this time, the GLOBAL_MESSAGE
and the GLOBAL_TIMESTAMP, same as before.
12:14:21.
And then, when I call f2 once and twice,
it'll also just reuse the existing variable.
That variable has been initialized now,
and it's the same as before.
So in f2, GLOBAL_TIMESTAMP, GLOBAL_TIMESTAMP.
At the end of my function here, I go back to main.
And at the end of main,
I just access the global variable from here as well.
Okay? So, GLOBAL_MESSAGE really is global.
And because it's public, I can access it in main as well.
Okay, and it says, by the way,
the GLOBAL_MESSAGE is this is a GLOBAL_MESSAGE.
There we are.
So, just to recap then,
you can declare global variables in Rust.
It's frowned upon because it kind of opens you up to misuse.
You have to use the static keyword.
And if you want to initialize it
with a fixed value at compile time, it's easy.
If you want to initialize it
with some kind of computed value, it's not so easy,
you basically have to use a Lazy wrapper,
and have some code
that can be executed thread safe by Rust
the first time you try to access the to initialize it.