Newbie problem with the borrow checker

Hi,

I'm working on micro service for learning purposes with tokio and warp. This micro service relies on other services which makes the response time an issue due to the accumulated latency of all its dependencies. So I wanted to implement a simple cache that will query the dependencies once per day and fill a in-memory cache. I've a stripped down version of that for testing in the rust playground:

use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};

struct Cache {
    cache: tokio::sync::RwLock<std::collections::HashMap<String, String>>,
}

impl Cache {
    pub fn new() -> Self {
        Cache {
            cache: tokio::sync::RwLock::new(std::collections::HashMap::new()),
        }
    }

    pub fn start(self) {
        tokio::spawn(async move {
            let mut i = 0;
            let mut interval = tokio::time::interval(tokio::time::Duration::from_millis(10));
            interval.tick().await;
            loop {
                let rand_string: String =
                    thread_rng().sample_iter(&Alphanumeric).take(30).collect();
                self.cache
                    .write()
                    .await
                    .insert(format!("{}", i), rand_string);
                interval.tick().await;
                i += 1;
            }
        });
    }

    pub async fn get(&self, key: &str) -> Option<String> {
        match self.cache.read().await.get(key) {
            Some(result) => Some(String::from(result)),
            None => None,
        }
    }
}

async fn use_cache(
    cache: std::sync::Arc<Cache>,
) -> tokio::task::JoinHandle<()> {
    tokio::spawn(async move {
        tokio::time::sleep(std::time::Duration::from_secs(5)).await;
        println!("{:?}", cache.get("1").await);
    })
}

#[tokio::main]
async fn main() {
    let cache = Cache::new();
    let shared_cache = std::sync::Arc::new(cache);
    let shared_cache = std::sync::Arc::clone(&shared_cache);
    shared_cache.start();

    let shared_cache = std::sync::Arc::clone(&shared_cache);
    use_cache(shared_cache).await;
}
Checking playground v0.0.1 (/playground)
error[E0507]: cannot move out of an `Arc`
  --> src/main.rs:55:5
   |
55 |     shared_cache.start();
   |     ^^^^^^^^^^^^ move occurs because value has type `Cache`, which does not implement the `Copy` trait

error: aborting due to previous error

For more information about this error, try `rustc --explain E0507`.
error: could not compile `playground`

I've tried to make the start method accept a &self but then I enter into lifetime issues as I need to specify a &'static lifetime due to tokio::spawn being also static. And to circumvent that I need to use Box::leak so cache is static (unless there is any other way to do that). But I still fall in the same trap :slight_smile:. But I guess that would be for another post!

Thanks!

Try this:

pub fn start(self: Arc<Self>) {
1 Like

Hi,

thanks for your suggestion. I tried it, but to no avail. I get more errors:

    Checking playground v0.0.1 (/playground)
error[E0382]: borrow of moved value: `shared_cache`
  --> src/main.rs:57:46
   |
54 |     let shared_cache = std::sync::Arc::clone(&shared_cache);
   |         ------------ move occurs because `shared_cache` has type `std::sync::Arc<Cache>`, which does not implement the `Copy` trait
55 |     shared_cache.start();
   |                  ------- `shared_cache` moved due to this method call
56 | 
57 |     let shared_cache = std::sync::Arc::clone(&shared_cache);
   |                                              ^^^^^^^^^^^^^ value borrowed here after move
   |
note: this function consumes the receiver `self` by taking ownership of it, which moves `shared_cache`
  --> src/main.rs:15:18
   |
15 |     pub fn start(self: std::sync::Arc<Self>) {
   |                  ^^^^

error: aborting due to previous error

Your third:

is trying to use the second one, that was moved out, but is still in scope. Surround the lines with braces to delimit the scope or name the second shared_cache differently.

Hi,

thank you @alice and @ssomers. I needed to apply both solutions (I cannot mark both of them as solutions).

@ssomers, I though that when you redefined a variable, the previous one ended its scope....

If you declare a new variable with the same name as an old one, it behaves exactly the same as if you give it a different name, except you can no longer talk about the old variable while the new one is in scope.

1 Like