Help with Tokio RwLock and the Copy Trait

I am trying to implement a simple health check using tokio for async that does a simple loop and runs an update function based on the result however I am running into an error with my RwLock.

struct HealthCheck {
    state: Arc<RwLock<ServerStatus>>,
}

enum ServerStatus {
    Ready {
        errors: Vec<i64>,
    },
    NotReady {
        ping_attempts: u8,
        ping_success: u8,
        ping_failure: u8,
    },
    Maintenance,
    Unhealthy,
}

...
...
// Init the state 
let state  =  Arc::new(RwLock::new(ServerStatus::NotReady {
    ping_attempts: 0,
    ping_success: 0,
    ping_failure: 0,
}));
let state_clone = state.clone();

// Spawn a task that loops taking the client
tokio::spawn(async move {
    let mut interval = time::interval(time::Duration::from_secs(5));
    loop {
        match client.ping().await {
            Ok(Ping) => {
                {
                    // Get a lock on the state 
                    let current_state = &state_clone.write().await;
                    // Update state
                    &current_state.update(Event::PingSuccess)
                }
            },
            Err(e) => {
                {
                    // Get a lock on the state 
                    let mut current_state = &state_clone.write().await;
                    // Update state
                    &current_state.update(Event::PingFailure)
                }
            }
        };         
        interval.tick().await;   
    }
});

impl ServerStatus {
    // This function handles the state machine for the health check of a server
    fn update(self, event: Event) -> ServerStatus 
}

The error is below

cannot move out of dereference of `tokio::sync::rwlock::RwLockWriteGuard<'_, health_check::ServerStatus>`
move occurs because value has type `health_check::ServerStatus`, which does not implement the `Copy` trait

error[E0507]: cannot move out of dereference of `tokio::sync::rwlock::RwLockWriteGuard<'_, health_check::ServerStatus>`
  --> src/health_check.rs:60:29
   |
60 | ...                   current_state.update(Event::PingSuccess)
   |                       ^^^^^^^^^^^^^ move occurs because value has type `health_check::ServerStatus`, which does not implement the `Copy` trait

Now I can make the error go away if I box the vector in the ready variant of ServerStatus but I don't understand why I need Copy to run the function of the write locked object? Is this the correct way to go about it or should I attempt a different approach ?

Your update function consumes the ServerStatus object, which means it has to be moved out of the RwLock before it can be called. This leaves the RwLock in an invalid state. If ServerStatus implemented Copy, the compiler would leave a copy in the RwLock and consume a separate copy.

In this case, however, you probably want to accept &mut self instead of self, which lets your function work on the copy that’s stored inside the RwLock directly.

1 Like

That makes sense, Thanks 2e71828