Burst
Burst

Reputation: 23

Rust: Closure composition ownership

To avoid nesting matches I am trying to use the and_then method for Result. The problem arises when I try to convert this

match Connection::open(&db_path) {
    Ok(conn) => {
        match conn.prepare("SELECT qwe,username_value,wer FROM my_table;") {
            Ok(mut stmt) => {
                match stmt.query_map([], |row| {Ok(row.get_unwrap::<usize, String>(1).clone())}) {
                    Ok(usernames) => {
                        for username in usernames {
                            if username.is_ok() {
                                println!("{}", username.expect("Could not read username value"));
                            }
                        }
                        println!("Query done");
                    },
                    Err(err) => println!("Error {}", err),
                }
            },
            Err(err) => println!("Error {}", err),
        }
    },
    Err(err) => println!("Error {}", err),
}

into this

match Connection::open(&db_path)
    .and_then(move |conn: Connection| conn.prepare("SELECT qwe,username_value,wer FROM my_table;"))
    .and_then(|mut stmt: Statement| stmt.query_map([], |row| {Ok(row.get_unwrap::<usize, String>(1).clone())})) {
    Ok(usernames) => {
        for username in usernames {
            if username.is_ok() {
                println!("{}", username.expect("Could not read username value"));
            }
        }
        println!("Query done");
    },
    Err(_) => println!("Error querying db"),
}

The SQL lib is rusqlite.

The first snippet is accepted by the compiler, the second one is not. The compiler screams at me with the following error message:

error[E0515]: cannot return value referencing function parameter `conn`
   --> src\main.rs:134:43
    |
134 |         .and_then(move |conn: Connection| conn.prepare("SELECT qwe,username_value,wer FROM my_table;"))
    |                                           ----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                                           |
    |                                           returns a value referencing data owned by the current function
    |                                           `conn` is borrowed here

error[E0515]: cannot return value referencing function parameter `stmt`
   --> src\main.rs:135:41
    |
135 |         .and_then(|mut stmt: Statement| stmt.query_map([], |row| {Ok(row.get_unwrap::<usize, String>(1).clone())})) {
    |                                         ----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                                         |
    |                                         returns a value referencing data owned by the current function
    |                                         `stmt` is borrowed here

error: aborting due to 2 previous errors

I tried with move on the three closures but the error message is the same. Why is the ownership wrong in the second snippet and which is the idiomatic way of writing something like this?

Upvotes: 0

Views: 126

Answers (1)

Jmb
Jmb

Reputation: 23319

The problem is that conn.prepare returns a statement that refers to conn, but conn is destroyed at the end of the closure so it's no longer here when the statement tries to use it. The idiomatic way to do what you want would be to use the ? operator:

let conn = Connection::open(&db_path)?;
let mut stmt = conn.prepare("SELECT qwe,username_value,wer FROM my_table;")?;
for username in stmt.query_map([], |row| {Ok(row.get_unwrap::<usize, String>(1).clone())})? {
    if username.is_ok() {
        println!("{}", username.expect("Could not read username value"));
    }
}

And let the caller handle the error. If you want to handle the error yourself, you can wrap the above code in a local function or closure (see also this question).

Upvotes: 1

Related Questions