AtomicU32 seemingly "forgets" about an updated value in a single-threaded environment

So I've been trying to muck around with atomics today, but I've hit a bizarre issue where if I update an AtomicU32 in the first iteration of a loop, and then try to access it in the second iteration, the second iteration only observes the initial value.

The code is as follows, and would ideally not panic.

use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::num::NonZeroU32;

fn main() {
    let aonz = AtomicOptionNonZeroU32::new(None);
    assert_eq!(aonz.get(), None);
    let val = NonZeroU32::new(13).unwrap();
    let mut first = true;
    loop {
        if first {
            assert_eq!(aonz.get(), Some(val));
            first = false;
        } else {
            assert_eq!(aonz.get(), Some(val));
            assert_eq!(aonz.take(), Some(val));
            assert_eq!(aonz.get(), None);

struct AtomicOptionNonZeroU32(AtomicU32);

impl AtomicOptionNonZeroU32 {
    fn new(ptr: Option<NonZeroU32>) -> Self {

    fn get(&self) -> Option<NonZeroU32> {

    fn set(&self, ptr: Option<NonZeroU32>) {
            .store(, Ordering::Release);

    fn take(&self) -> Option<NonZeroU32> {
        NonZeroU32::new(self.0.swap(0, Ordering::SeqCst))

playground link

It's the third iteration.

Your problem is not with the second iteration but with the third: in the second you take the value and leave None in place.

1 Like

I suppose that means my attempt at creating an MRE was unsuccessful, as the code this is based on does have the issue described in the second iteration. Guess I'll have to try again, or just post the whole thing.

Welp, it seems like the real issue was me using NonZeroU32::new_unchecked in a case where a 0 could slip through. Thanks a bunch for trying to help, though :slight_smile: