Move occurs because `conn` has type `rusqlite::Connection`

Hello everyone,

I am trying to create a service struct which acts as a central access point for the application i am working on. Complet example:

extern crate rusqlite;
use rusqlite::Connection;
use std::process;

struct StateManager {
    dbconn: Connection,
}

struct Service {
    dbconn: Connection,
    state_manager: StateManager,
}

impl Service {
    pub fn new() -> Service {
        let conn = match Connection::open("test.db") {
            Ok(conn) => conn,
            Err(e) => {
                eprintln!("Could not connect to database: {}", e);
                process::exit(1)
            }
        };

        Service {
            dbconn: conn,
            state_manager: StateManager { dbconn: conn },
        }
    }
}

fn main() {
    let _service = Service::new();
}

However, this version fails to compile:

error[E0382]: use of moved value: `conn`
  --> src/main.rs:26:51
   |
16 |         let conn = match Connection::open("test.db") {
   |             ---- move occurs because `conn` has type `rusqlite::Connection`, which does not implement the `Copy` trait
...
25 |             dbconn: conn,
   |                     ---- value moved here
26 |             state_manager: StateManager { dbconn: conn },
   |                                                   ^^^^ value used here after move

Changing my code to take a reference of the connection:

struct StateManager<'a> {
    dbconn: &'a Connection,
}

struct Service<'a> {
    dbconn: &'a Connection,
    state_manager: &'a StateManager<'a>,
}

impl<'a> Service<'a> {
    pub fn new() -> Service<'a> {
        let conn = match Connection::open("test.db") {
            Ok(conn) => conn,
            Err(e) => {
                eprintln!("Could not connect to database: {}", e);
                process::exit(1)
            }
        };

        Service {
            dbconn: &conn,
            state_manager: &StateManager { dbconn: &conn },
        }
    }
}

Fails to compile with this:

  --> src/main.rs:24:9
   |
24 | /         Service {
25 | |             dbconn: &conn,
   | |                     ----- `conn` is borrowed here
26 | |             state_manager: &StateManager { dbconn: &conn },
27 | |         }
   | |_________^ returns a value referencing data owned by the current function

The question i have is, because connection does not implement the Copy trait, how would the connection be constructed using a factory function and be returned if copy is not allowed? The solution for me is to construct the connection in main and pass it around. However, the separation-of-concerns concept resides with me and i'd like to fix the code above somehow to keep the main "clean".

My understanding of lifetimes and borrow checking is still blurry and i am sure i am missing something here.

Any help / suggestions would be very much appreciated.

Thanks

There are 2 problems you will run into trying to get this to work.

  1. Service is self referencing indirectly because of StateManager this will become a problem later, for now the reason the compiler complains about lifetimes in Service::new is that it has nothing to tie the return lifetime too
// will fail to compile
fn test() -> &str {
    ""
}
// this will compile now the compiler knows what to tie the lifetime to
fn test(_: &bool) -> &str {
    ""
}
  1. the Connection doesn't impl Copy like you said it also doesn't imp Clone which would make this whole thing a lot easier.

You could use a Rc to keep references to the Connection and hold the original in Service or StateManager

Here is one idea, it compiles all the way until it tries to link the sqlite lib using RC

1 Like

Thank you so much @DevinR528 . I reflected what you did on my version of the code:

Service {
            dbconn: conn,
            state_manager: StateManager {
                dbconn: Rc::clone(&conn),
            },
        }

Apparently here, &conn is being "borrowed of moved value". However, using your version:

let state_manager = StateManager {
            dbconn: Rc::clone(&conn),
        };
        Service {
            dbconn: conn,
            state_manager,
        }

What is the technical explanation behind this? it's a bit confusing to be honest.

For the "borrow of moved value" think of it like this

Service {
    // dbconn takes ownersship of "conn"
    dbconn: conn, 
    // conn no longer exists, so we cannot take a ref
    // of a "non existant value"
    state_manager: StateManager { dbconn: Rc::clone(&conn) },
}

so if you borrowing before conn is moved into dbconn rust knows its still valid at the point of borrowing it.

1 Like