Lifetime annotation problem and how to access value inside Arc<Mutex<>>>

I have several questions regarding lifetime annotation.

  1. I accidentally fix the error borrowed data escapes outside of function requirement occurs because of the type std::sync::Mutex<&str>, which makes the generic argument &str invariant the struct std::sync::Mutex<T> is invariant over the parameter T thrown if without where 'a: 'static. I looks like related to static lifetime annotation. I suppose it's because the str (in holder var) created in the main function lives for the entire duration of program, but I could be wrong. If commenting out where and 'a: 'static clause, I can't use Arc::new(holder.clone()) as workaround. I think there is something I don't know.

  2. The program went panic with the error thread 'main' panicked at quinn/examples/p2p.rs:23:15: called Result::unwrap()on anErr value: Mutex { data: "xyz", poisoned: false, .. } stack backtrace:. It seems to me the result is not yet completed, but I can't call something like into_inner().await. How to fix this?

Many thanks.

use std::sync::Arc;
use std::sync::Mutex;
fn f<'a>(holder: Arc<Mutex<&'a str>>) -> Arc<Mutex<&'a str>> 
where 
    'a: 'static
{
    let holder1 = holder.clone();
    tokio::spawn(async move {
        let mut value = holder1.lock().unwrap();
        *value = "abc";
    });
    holder
}

#[tokio::main]
async fn main() {
    let holder = Arc::new(Mutex::new("xyz"));
    let new_v = f(holder);
    let a = Arc::try_unwrap(new_v);
    let b = a.unwrap();
    println!("{:?}", b.into_inner().unwrap());
}

The &str type is used for strings that you do not own. In the case of an Arc<Mutex<string type goes here>>, you're always going to want ownership. The type that has ownership over the string is String, so you should instead use Arc<Mutex<String>>.

As for &'static str, that's a type that can only be used for string constants hard-coded in your source code. It's rarely what you want.

The Arc/Mutex type provides no way to wait for the value to change.

If that's what you want, then you should do something else. A few options:

  • Await the JoinHandle returned by tokio::spawn to wait for the background task to exit.
  • Use a tokio::sync::oneshot channel to deliver the message with a message passing channel.
  • Use a tokio::sync::watch channel, which is similar to Arc/Mutex except that it allows you to wait for the value to change.

Sorry I still do not understand. Let me rephrase to see something that I miss.

From other doc like [1], it looks like the error comes from my original function signature fn f<'a>(holder: Arc<Mutex<&'a str>>) -> Arc<Mutex<&'a str>> parameter (holder: Arc<Mutex<&'a str>>) should be holder: Arc<Mutex<&'static str>> instead, because that value is created and survive the entire lifetime of the program. Am I correct?

I attempt to change the code to something as below. Although it compiles, I do know how to retrieve the value inside the holder.

fn f(holder: Arc<Mutex<String>>) {
    tokio::spawn(async move {
        let mut value = holder.lock().unwrap();
        *value = String::from("abc");
    });
}

#[tokio::main]
async fn main() {
    let binding = String::from("xyz");
    let holder = Arc::new(Mutex::new(binding));
    f(holder);
    // println!("{:?}", holder); // this throws borrow of moved value: `holder`
}

Thanks for helping answer my questions.

[1]. Static - Rust By Example

You should pass a clone of the arc:

f(holder.clone());

The Arc type has the special feature that when you clone it, you get a new reference to the same underlying object.

Using &'static str will work. It's just that a &'static str is much more limited than a String. The String type can hold any string, whereas &'static str can only hold strings that are hard-coded in your source code.

You can think of &'static str as "reference to string data owned by an immutable global variable", because ultimately, when you type "abc", the compiler will convert this into an immutable global variable holding the bytes [b'a', b'b', b'c'] and then create a reference to that immutable global variable.

I can get the code working now, but I feel I have fundamental misconceptions mixing several things together such as lifetime, scope, shared references. I will come back reviewing this again, because I find I can't even point out which part I do not understand, though I have vague ideas. Thanks for patiently answering my questions. I appreciate it!

async fn spawn_task(holder: Arc<Mutex<String>>) -> Result<String, JoinError> {
    let handle = tokio::spawn(async move {
        let v = holder.lock().unwrap();
        let mut inside_v = v.clone();
        inside_v += "abc";
        inside_v
    });
    handle.await
 }

#[tokio::main]
async fn main() {
    let a = String::from("xyz");
    let arc = Arc::new(Mutex::new(a));
    let myf = spawn_task(arc);
    println!("{:?}", myf.await)
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.