Mutable/Imutable error

I have this code and it is with an error:


#![no_std]
#![no_main]

use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::peripherals::{PA1, PA2, PA3, PA4};
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embassy_executor::Spawner;
use embassy_stm32::interrupt;  
use embassy_time::{Duration, Timer};
use embassy_executor::{Executor, InterruptExecutor};  
use embassy_stm32::interrupt::{InterruptExt, Priority};  
use static_cell::StaticCell;



static LED1: Mutex<ThreadModeRawMutex, Option<Output<'static, PA1>>> = Mutex::new(None);
static LED2: Mutex<ThreadModeRawMutex, Option<Output<'static, PA2>>> = Mutex::new(None);
static LED3: Mutex<ThreadModeRawMutex, Option<Output<'static, PA3>>> = Mutex::new(None);
static LED4: Mutex<ThreadModeRawMutex, Option<Output<'static, PA4>>> = Mutex::new(None);

#[embassy_executor::task]
async fn run_high() {
    loop {
        LED1.lock(|led1| {
            if let Some(l1) = led1.as_mut() {
                l1.set_high(); // Chamada direta
            }
        });
        LED2.lock(|led2| {
            if let Some(l2) = led2.as_mut() {
                l2.set_low();
            }
        });
        LED3.lock(|led3| {
            if let Some(l3) = led3.as_mut() {
                l3.set_low();
            }
        });
        LED4.lock(|led4| {
            if let Some(l4) = led4.as_mut() {
                l4.set_low();
            }
        });

        Timer::after(Duration::from_millis(300)).await;
    }
}

#[embassy_executor::task]
async fn run_med() {
    loop {
        LED1.lock(|led1| {
            if let Some(l1) = led1.as_mut() {
                l1.set_low();
            }
        });
        LED2.lock(|led2| {
            if let Some(l2) = led2.as_mut() {
                l2.set_high();
            }
        });
        LED3.lock(|led3| {
            if let Some(l3) = led3.as_mut() {
                l3.set_low();
            }
        });
        LED4.lock(|led4| {
            if let Some(l4) = led4.as_mut() {
                l4.set_low();
            }
        });

        Timer::after(Duration::from_millis(400)).await;
    }
}

#[embassy_executor::task]
async fn run_low() {
    loop {
        LED1.lock(|led1| {
            if let Some(l1) = led1.as_mut() {
                l1.set_low();
            }
        });
        LED2.lock(|led2| {
            if let Some(l2) = led2.as_mut() {
                l2.set_low();
            }
        });
        LED3.lock(|led3| {
            if let Some(l3) = led3.as_mut() {
                l3.set_high();
            }
        });
        LED4.lock(|led4| {
            if let Some(l4) = led4.as_mut() {
                l4.set_low();
            }
        });

        Timer::after(Duration::from_millis(500)).await;
    }
}

#[cortex_m_rt::entry]
fn main() -> ! {
    let p = embassy_stm32::init(Default::default());

    
    LED1.lock(|led1| {
        *led1 = Some(Output::new(p.PA1, Level::Low, Speed::Low)); 
    });
    LED2.lock(|led2| {
        *led2 = Some(Output::new(p.PA2, Level::Low, Speed::Low));
    });
    LED3.lock(|led3| {
        *led3 = Some(Output::new(p.PA3, Level::Low, Speed::Low));
    });
    LED4.lock(|led4| {
        *led4 = Some(Output::new(p.PA4, Level::Low, Speed::Low));
    });

 


static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();  
static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();  
static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();  



    #[interrupt]  
unsafe fn USART1() {  
    EXECUTOR_HIGH.on_interrupt()  
}  

#[interrupt]  
unsafe fn USART2() {  
    EXECUTOR_MED.on_interrupt()  
}


    // Start LED initialization in a separate task  
    let executor = EXECUTOR_LOW.init(Executor::new());  
 

        // Configure interrupts for high and medium tasks  
        interrupt::USART1.set_priority(interrupt::Priority::P6);  
        let spawner_high = EXECUTOR_HIGH.start(interrupt::USART1);  
        spawner_high.spawn(run_high()).unwrap();  

        interrupt::USART2.set_priority(interrupt::Priority::P7);  
        let spawner_med = EXECUTOR_MED.start(interrupt::USART2);  
        spawner_med.spawn(run_med()).unwrap();  
        
        executor.run(|spawner| {
            spawner.spawn(run_low());
        });

    
}

error:

error[E0596]: cannot borrow `*led1` as mutable, as it is behind a `&` reference
   --> src/main.rs:598:31
    |
597 |         LED1.lock(|led1| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA1>>`
598 |             if let Some(l1) = led1.as_mut() {
    |                               ^^^^ `led1` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led2` as mutable, as it is behind a `&` reference
   --> src/main.rs:603:31
    |
602 |         LED2.lock(|led2| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA2>>`
603 |             if let Some(l2) = led2.as_mut() {
    |                               ^^^^ `led2` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led3` as mutable, as it is behind a `&` reference
   --> src/main.rs:608:31
    |
607 |         LED3.lock(|led3| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA3>>`
608 |             if let Some(l3) = led3.as_mut() {
    |                               ^^^^ `led3` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led4` as mutable, as it is behind a `&` reference
   --> src/main.rs:613:31
    |
612 |         LED4.lock(|led4| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA4>>`
613 |             if let Some(l4) = led4.as_mut() {
    |                               ^^^^ `led4` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led1` as mutable, as it is behind a `&` reference
   --> src/main.rs:626:31
    |
625 |         LED1.lock(|led1| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA1>>`
626 |             if let Some(l1) = led1.as_mut() {
    |                               ^^^^ `led1` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led2` as mutable, as it is behind a `&` reference
   --> src/main.rs:631:31
    |
630 |         LED2.lock(|led2| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA2>>`
631 |             if let Some(l2) = led2.as_mut() {
    |                               ^^^^ `led2` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led3` as mutable, as it is behind a `&` reference
   --> src/main.rs:636:31
    |
635 |         LED3.lock(|led3| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA3>>`
636 |             if let Some(l3) = led3.as_mut() {
    |                               ^^^^ `led3` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led4` as mutable, as it is behind a `&` reference
   --> src/main.rs:641:31
    |
640 |         LED4.lock(|led4| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA4>>`
641 |             if let Some(l4) = led4.as_mut() {
    |                               ^^^^ `led4` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led1` as mutable, as it is behind a `&` reference
   --> src/main.rs:654:31
    |
653 |         LED1.lock(|led1| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA1>>`
654 |             if let Some(l1) = led1.as_mut() {
    |                               ^^^^ `led1` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led2` as mutable, as it is behind a `&` reference
   --> src/main.rs:659:31
    |
658 |         LED2.lock(|led2| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA2>>`
659 |             if let Some(l2) = led2.as_mut() {
    |                               ^^^^ `led2` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led3` as mutable, as it is behind a `&` reference
   --> src/main.rs:664:31
    |
663 |         LED3.lock(|led3| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA3>>`
664 |             if let Some(l3) = led3.as_mut() {
    |                               ^^^^ `led3` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*led4` as mutable, as it is behind a `&` reference
   --> src/main.rs:669:31
    |
668 |         LED4.lock(|led4| {
    |                    ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA4>>`
669 |             if let Some(l4) = led4.as_mut() {
    |                               ^^^^ `led4` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0594]: cannot assign to `*led1`, which is behind a `&` reference
   --> src/main.rs:684:9
    |
683 |     LED1.lock(|led1| {
    |                ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA1>>`
684 |         *led1 = Some(Output::new(p.PA1, Level::Low, Speed::Low));
    |         ^^^^^ `led1` is a `&` reference, so the data it refers to cannot be written

error[E0594]: cannot assign to `*led2`, which is behind a `&` reference
   --> src/main.rs:687:9
    |
686 |     LED2.lock(|led2| {
    |                ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA2>>`
687 |         *led2 = Some(Output::new(p.PA2, Level::Low, Speed::Low));
    |         ^^^^^ `led2` is a `&` reference, so the data it refers to cannot be written

error[E0594]: cannot assign to `*led3`, which is behind a `&` reference
   --> src/main.rs:690:9
    |
689 |     LED3.lock(|led3| {
    |                ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA3>>`
690 |         *led3 = Some(Output::new(p.PA3, Level::Low, Speed::Low));
    |         ^^^^^ `led3` is a `&` reference, so the data it refers to cannot be written

error[E0594]: cannot assign to `*led4`, which is behind a `&` reference
   --> src/main.rs:693:9
    |
692 |     LED4.lock(|led4| {
    |                ---- consider changing this binding's type to be: `&mut Option<Output<'_, PA4>>`
693 |         *led4 = Some(Output::new(p.PA4, Level::Low, Speed::Low));
    |         ^^^^^ `led4` is a `&` reference, so the data it refers to cannot be written

error: aborting due to 16 previous errors; 2 warnings emitted

Some errors have detailed explanations: E0594, E0596.
For more information about an error, try `rustc --explain E0594`.
       Error Failed to run cargo build: exit code = Some(101).

Can Someone please help me I really need this code but it never works. I don't know what else do to. The problem this in the mut parte of the LEDs. Thank you!

for some reason the specific mutex you are using only provides a shared reference to the synchronized data. this is quite strange, I would recommend either using a different mutex, or wrapping your peripherals in Cell or UnsafeCell.

alternativly, if you could build your peripherals out of Atomic* types, you could mutate them through a shared reference.

I'm grateful for your help, but what I really need is a code similar to this. Now I tried to initialize the leds inside an async function but it's not returning any error but is also not working:


#![no_std]
#![no_main]

use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::interrupt;
use embassy_stm32::peripherals::{PA1, PA2, PA3, PA4};
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
use embassy_sync::mutex::Mutex;
use embassy_time::{Duration, Timer};
use static_cell::StaticCell;
use embassy_executor::{Executor, InterruptExecutor};
use embassy_stm32::interrupt::{InterruptExt, Priority};
use panic_halt as _;

// Define mutexes for each LED
static LED1: Mutex<ThreadModeRawMutex, Option<Output<'static, PA1>>> = Mutex::new(None);
static LED2: Mutex<ThreadModeRawMutex, Option<Output<'static, PA2>>> = Mutex::new(None);
static LED3: Mutex<ThreadModeRawMutex, Option<Output<'static, PA3>>> = Mutex::new(None);
static LED4: Mutex<ThreadModeRawMutex, Option<Output<'static, PA4>>> = Mutex::new(None);

#[embassy_executor::task]
async fn initialize_leds(p: embassy_stm32::Peripherals) {
    // Initialize LEDs
    let led1 = Output::new(p.PA1, Level::Low, Speed::Low);
    let led2 = Output::new(p.PA2, Level::Low, Speed::Low);
    let led3 = Output::new(p.PA3, Level::Low, Speed::Low);
    let led4 = Output::new(p.PA4, Level::Low, Speed::Low);
    
    // Store LEDs in mutexes for safe access
    {
        let mut led1_lock = LED1.lock().await;
        *led1_lock = Some(led1);
    }
    {
        let mut led2_lock = LED2.lock().await;
        *led2_lock = Some(led2);
    }
    {
        let mut led3_lock = LED3.lock().await;
        *led3_lock = Some(led3);
    }
    {
        let mut led4_lock = LED4.lock().await;
        *led4_lock = Some(led4);
    }
}

// Task to run with high priority
#[embassy_executor::task]
async fn run_high() {
    let mut led1 = LED1.lock().await;
    led1.as_mut().unwrap().set_high(); // Acende LED1
    Timer::after(Duration::from_millis(300)).await; // Aguarda 300 ms
    led1.as_mut().unwrap().set_low(); // Apaga LED1
}

// Task to run with medium priority
#[embassy_executor::task]
async fn run_med() {
    let mut led2 = LED2.lock().await;
    led2.as_mut().unwrap().set_high(); // Acende LED2
    Timer::after(Duration::from_millis(300)).await; // Aguarda 300 ms
    led2.as_mut().unwrap().set_low(); // Apaga LED2
}

// Task to run with low priority
#[embassy_executor::task]
async fn run_low() {
    let mut led3 = LED3.lock().await;
    led3.as_mut().unwrap().set_high(); // Acende LED3
    Timer::after(Duration::from_millis(300)).await; // Aguarda 300 ms
    led3.as_mut().unwrap().set_low(); // Apaga LED3
}

static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();
static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();
static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();

#[interrupt]
unsafe fn USART1() {
    EXECUTOR_HIGH.on_interrupt();
}

#[interrupt]
unsafe fn USART2() {
    EXECUTOR_MED.on_interrupt();
}

#[cortex_m_rt::entry]
fn main() -> ! {
    let p = embassy_stm32::init(Default::default());

    // Start LED initialization in a separate task
    let executor = EXECUTOR_LOW.init(Executor::new());
    executor.run(|spawner| {
        spawner.spawn(initialize_leds(p)).unwrap();

        // Configure interrupts for high and medium tasks
        interrupt::USART1.set_priority(interrupt::Priority::P6);
        let spawner_high = EXECUTOR_HIGH.start(interrupt::USART1);
        spawner_high.spawn(run_high()).unwrap();

        interrupt::USART2.set_priority(interrupt::Priority::P7);
        let spawner_med = EXECUTOR_MED.start(interrupt::USART2);
        spawner_med.spawn(run_med()).unwrap();
        
        // Spawn the low priority task last
        spawner.spawn(run_low()).unwrap();
    });
}

Please help me I couldn't find a solution

  1. you seem like you're trying to jump into doing the whole thing instead of building it up piece by piece. I would try to actuate 1 LED to make sure the hardware interface is working before doing anything else.
  2. you're using a lot of async and i don't seem much await. futures generally don't do anything unless awaited. the compiler should generally give you warnings if you are missing some.
  3. you're probably gonna want some debugging facilities, even if it's just shoving bytes across a serial port.
1 Like

this is by design and mostly for bare metal targets (because bare metal targets has different trade-off considerations than hosted environments). for example, the widely used critical_section::Mutex explicitly documents this behavior.

quote from the documentation

std::sync::Mutex has two purposes. It converts types that are Send but not Sync into types that are both; and it provides interior mutability. critical_section::Mutex, on the other hand, only adds Sync. It does not provide interior mutability.

This was a conscious design choice. It is possible to create multiple CriticalSection tokens, either by nesting critical sections or Copying an existing token. As a result, it would not be sound for Mutex::borrow to return &mut T, because there would be nothing to prevent calling borrow multiple times to create aliased &mut T references.

The solution is to include a runtime check to ensure that each resource is borrowed only once. This is what std::sync::Mutex does. However, this is a runtime cost that may not be required in all circumstances. For instance, Mutex<Cell<T>> never needs to create &mut T or equivalent.

If &mut T is needed, the simplest solution is to use Mutex<RefCell<T>>, which is the closest analogy to std::sync::Mutex. RefCell inserts the exact runtime check necessary to guarantee that the &mut T reference is unique.

To reduce verbosity when using Mutex<RefCell<T>>, we reimplement some of RefCell’s methods on it directly.

in OP's case, it's a Mutex from embassy_sync, but the principle is the same, as documented at the RawMutex trait.

quote from the documentation

Note that, unlike other mutexes, implementations only guarantee no concurrent access from other threads: concurrent access from the current thread is allowed. For example, it’s possible to lock the same mutex multiple times reentrantly.

Therefore, locking a RawMutex is only enough to guarantee safe shared (&) access to the data, it is not enough to guarantee exclusive (&mut) access.

the solution, as you already pointed, is to use interior mutability wrappers, typically RefCell (safe but with a little overhead) or UnsafeCell (no runtime overhead but need unsafe). Cell is not usable in this case because the shared states are access to hardware peripherals which are not Copy (obviously).


first, your tasks are running on different executors and within multiple interrupts, so the ThreadModeRawMutex is the wrong type to use, you should use CriticalSectionRawMutex instead, or you can just use the type alias CriticalSectionMutex.

then, your tasks are not run in a loop so they will run once and finish (your original code did it right).

2 Likes