Skip to content

Conversation

@EvansJahja
Copy link

Closes #389


But this has a few problems. It is a mutable global variable, and in Rust, these are always unsafe to interact with. These variables are also visible across your whole program, which means the borrow checker is unable to help you track references and ownership of these variables.

Up until Rust 2024, the usage of static **mutable** variable has been discouraged. Since Rust 2024, static mutable variable results in an error. Of course, there are legitimate use case of having a shared mutable state, it's just that Rust want you to really think about the scope of the mutability. We'll talk about this later when we talk about interior mutability.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When read literally, "Since Rust 2024, static mutable variable results in an error." is wrong.

A static mutable variable in itself is not an issue at all, if you are comfortable with unsafe code: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=a9bb1d76aa1ef7cb07442d890438cd7d

What's discouraged is using references to static mutable variables:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=7b96c2c8023526d86d8f9b619eda9846
And even there, it's not a hard error, but a deny lint, that can be overridden (#[allow(static_mut_refs)])

Perhaps make that "Since Rust 2024, references to static mutable variables are denied by default."?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in d918f02, thank you for the correction


Of course, none of the above are safe. In order to get a proper interior mutability that is safe, we should implement `Sync` ourself and put in our synchronization primitive specific to the hardware such as locking the resource with atomic swap and guarding the code with interrupt-free section (disable interrupt, run some code, enable interrupt), also known as critical section.

You could check how [rust-console gba](https://github.com/rust-console/gba/blob/6a3fcc1ee6493a499af507f8394ee542500721b7/src/gba_cell.rs#L63) implement it for reference.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this example a bit too specific and complicated?
I don't have a better example at hand though. Not a big deal, if someone has a better suggestion, it can easily be replaced later.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, let's keep this for now

@jannic
Copy link
Member

jannic commented May 24, 2025

I very much like what you wrote, it explains the topic well and is easily understandable!

What I did not yet check is how well this section fits into the book. I only read the diff in isolation, and the last time I read the book was a long time ago, so that should be judged by someone more familiar with the book.

@EvansJahja
Copy link
Author

Bumping this as there doesn't seem to be any activity. Perhaps @jannic could pitch in if there's anything else we need?

Copy link
Member

@eldruin eldruin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! Thank you so much!
I just did some wording adjustments and added some links.
Once that is settled, I would merge it.


A `static` variable can be borrowed as `&foo` without much issue as it is just reading.

The problem occurs when we try to borrow is as mutable. Consider the following:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The problem occurs when we try to borrow is as mutable. Consider the following:
The problem occurs when we try to borrow it as mutable. Consider the following:


The question is, which one would run? It's not obviousy just looking at a code. Perhaps it depends on the order or execution, which would be guaranteed _if_ there's only one active execution at a time.

You may think that because you're in an embedded programming land and you only have single core, you don't have to think about race condition, but even with a single core CPU without making any thread, you may have interrupts and those interrupt requests may access the shared mutable variable `flag`. The issue here is that the compiler cannot prohibit you from taking multiple mutable reference to the shared mutable variable at compile time, especially since it does not know about the order of executions of threads or interrupts, for example.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
You may think that because you're in an embedded programming land and you only have single core, you don't have to think about race condition, but even with a single core CPU without making any thread, you may have interrupts and those interrupt requests may access the shared mutable variable `flag`. The issue here is that the compiler cannot prohibit you from taking multiple mutable reference to the shared mutable variable at compile time, especially since it does not know about the order of executions of threads or interrupts, for example.
You may think that because you're in an embedded programming context and you only have a single core, you don't have to think about race conditions, but even with a single core CPU without making any threads, you may have interrupts and those interrupt requests may access the shared mutable variable `flag`. The issue here is that the compiler cannot prohibit you from taking multiple mutable reference to the shared mutable variable at compile time, especially since it does not know about the order of execution of threads or interrupts, for example.


You may think that because you're in an embedded programming land and you only have single core, you don't have to think about race condition, but even with a single core CPU without making any thread, you may have interrupts and those interrupt requests may access the shared mutable variable `flag`. The issue here is that the compiler cannot prohibit you from taking multiple mutable reference to the shared mutable variable at compile time, especially since it does not know about the order of executions of threads or interrupts, for example.

Also, the use of `static mut` variables _might_ be deprecated in the future. There's a strong indication of this and having the usage `deny` by default since Rust 2024 is one of them.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Also, the use of `static mut` variables _might_ be deprecated in the future. There's a strong indication of this and having the usage `deny` by default since Rust 2024 is one of them.
Also, the use of `static mut` variables _might_ be deprecated in the future. An indication is that since Rust 2024, references to static mutable variables are lint-denied by default.

Let's keep it more vague, since we do not know what will happen.


Interior mutability is a way to "trick" the borrow checker to mutate a borrowed reference `&foo` (notice the lack of `&mut`). This is basically us saying to the compiler "I'm going to mutate the shared reference anyway, please don't stop me.".

Of course, mutating something that is borrowed in other places are undefined behaviors (UB), yet there is a way of doing so, using an `UnsafeCell`. As the name implies, you need to use the `unsafe` keyword when using it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Of course, mutating something that is borrowed in other places are undefined behaviors (UB), yet there is a way of doing so, using an `UnsafeCell`. As the name implies, you need to use the `unsafe` keyword when using it.
Of course, mutating something that is borrowed in other places is undefined behavior (UB), yet there is a way of doing so, using an [`UnsafeCell`](https://doc.rust-lang.org/core/cell/struct.UnsafeCell.html). As the name implies, you need to use the `unsafe` keyword when using it.


## So what is interior mutability?

Interior mutability is a way to "trick" the borrow checker to mutate a borrowed reference `&foo` (notice the lack of `&mut`). This is basically us saying to the compiler "I'm going to mutate the shared reference anyway, please don't stop me.".
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Interior mutability is a way to "trick" the borrow checker to mutate a borrowed reference `&foo` (notice the lack of `&mut`). This is basically us saying to the compiler "I'm going to mutate the shared reference anyway, please don't stop me.".
Interior mutability is a way around the borrow checker to mutate a borrowed reference `&foo` (notice the lack of `&mut`). This is basically us saying to the compiler "I'm going to mutate the shared reference anyway, please don't stop me.".

I think "trick" sounds a bit too alluring :) Since we do not want people to use unsafe more that really strictly necessary, I would make the wording slightly more neutral.

```

where Sync does absolutely nothing.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
where Sync does absolutely nothing.
where `Sync` does absolutely nothing.


where Sync does absolutely nothing.

Another way is to use Rust's `SyncUnsafeCell` which is currently only available in nightly under the flag `#![feature(sync_unsafe_cell)]`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Another way is to use Rust's `SyncUnsafeCell` which is currently only available in nightly under the flag `#![feature(sync_unsafe_cell)]`.
Another way is to use Rust's [`SyncUnsafeCell`](https://doc.rust-lang.org/core/cell/struct.SyncUnsafeCell.html) which is currently only available in nightly under the flag `#![feature(sync_unsafe_cell)]` as of this writing.

Another way is to use Rust's `SyncUnsafeCell` which is currently only available in nightly under the flag `#![feature(sync_unsafe_cell)]`.


Of course, none of the above are safe. In order to get a proper interior mutability that is safe, we should implement `Sync` ourself and put in our synchronization primitive specific to the hardware such as locking the resource with atomic swap and guarding the code with interrupt-free section (disable interrupt, run some code, enable interrupt), also known as critical section.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Of course, none of the above are safe. In order to get a proper interior mutability that is safe, we should implement `Sync` ourself and put in our synchronization primitive specific to the hardware such as locking the resource with atomic swap and guarding the code with interrupt-free section (disable interrupt, run some code, enable interrupt), also known as critical section.
Of course, none of the above is safe. In order to get proper interior mutability that is safe, we should implement `Sync` ourselves and put in our synchronization primitives specific to the hardware such as locking the resource with atomic swap and guarding the code with an interrupt-free section (disable interrupt, run some code, enable interrupt), also known as [critical section](https://github.com/rust-embedded/critical-section).


Of course, none of the above are safe. In order to get a proper interior mutability that is safe, we should implement `Sync` ourself and put in our synchronization primitive specific to the hardware such as locking the resource with atomic swap and guarding the code with interrupt-free section (disable interrupt, run some code, enable interrupt), also known as critical section.

You could check how [rust-console gba](https://github.com/rust-console/gba/blob/6a3fcc1ee6493a499af507f8394ee542500721b7/src/gba_cell.rs#L63) implement it for reference.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
You could check how [rust-console gba](https://github.com/rust-console/gba/blob/6a3fcc1ee6493a499af507f8394ee542500721b7/src/gba_cell.rs#L63) implement it for reference.
You could check how [rust-console gba](https://github.com/rust-console/gba/blob/6a3fcc1ee6493a499af507f8394ee542500721b7/src/gba_cell.rs#L63) implement it for reference (this example if fairly complex, though).


You could check how [rust-console gba](https://github.com/rust-console/gba/blob/6a3fcc1ee6493a499af507f8394ee542500721b7/src/gba_cell.rs#L63) implement it for reference.

Interior mutability and static mutability is a rather deep topic. I strongly recommend reading more about static mutable references from [the rust edition guide here](https://github.com/rust-lang/edition-guide/blob/master/src/rust-2024/static-mut-references.md).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Interior mutability and static mutability is a rather deep topic. I strongly recommend reading more about static mutable references from [the rust edition guide here](https://github.com/rust-lang/edition-guide/blob/master/src/rust-2024/static-mut-references.md).
Interior mutability and static mutability is a rather deep topic. I strongly recommend reading more about static mutable references from [The Rust Edition Guide here](https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Discourage use of static mut in singleton

3 participants