Keeping variable from macro in scope of caller


I am calling a function from a thread with a signature of

type ElementDataReceiver = Option<Arc<Mutex<Receiver<ElementStreamFormat>>>>;

fn run(&self, input: ElementDataReceiver) -> Result<()> 

There will be a lot of these functions so getting a mutex guard from input will be a pain so I wanted to make a macro to do this.

I have something like this

    macro_rules! unlock_or_none {
            ($var:expr) => {
                    let arc = $var.and_then(|arc| Arc::try_unwrap(arc).ok());
                    let mutex_guard : Option<MutexGuard<Receiver<ElementStreamFormat>>> = arc.and_then(|mutex| mutex.lock().ok());

In run I want to unlock the passed in parameter with

let input1_lock = unlock_or_none!(input);

If any part on the unlock fails I want input1_lock to be None

My run function will be something like

    fn run(&self,input: ElementDataReceiver) -> Result<()> {

     let input1_lock = unlock_or_none!(input1);
     loop {

                match input1_lock {
                    Some(lock) => {  
                       let val1 = lock.recv().unwrap();
                    None    => {},

This results in rc.and_then(|mutex| mutex.lock().ok());
| ----- ^ mutex dropped here while still borrowed

I understand why but was wondering if there was a better way to construct this macro so the mutex remains in scope to the caller ?



Are you sure you want to use the Arc::try_unwrap here? This function returns Ok only when this Arc was the only pointer to the mutex. In that case, you have the Mutex by value, so there’s no point in calling lock, you can just use into_inner instead. That way, your macro can just return fully owned Option<T>, with no need for storing a mutex. Also, this could be written as a function, no macros necessary.

If try_unwrap is not the semantics you want, perhaps you want this? (Note the as_ref which converts &Option<X> to Option<&X>)

$var.as_ref().and_then(|mutex| mutex.lock().ok())

Ps. This should get you syntax coloring

code here


No you were right. I originally had as_ref() but forgot to switch back when experimenting.