I'm attempting to open and write to a database in a Rust library I will call from python, with the help of pyo3. If an error occurs, I would like to raise an exception that can be caught in the calling Python process, but I'm having difficulties terminating execution and raising an error.
use rusqlite::{Connection};
use rusqlite::NO_PARAMS;
use pyo3::{Python, wrap_pyfunction};
use pyo3::exceptions::PyIOError;
#[pyfunction]
fn do_something(_py: Python) -> PyResult<u32> {
match Connection::open("database.sql") {
Ok(t) => conn = t,
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py)
}
};
match conn.execute(
"create table if not exists cats (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
)",
NO_PARAMS,
) {
Ok(_t) => (),
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py)
}
}
Ok(0)
It's my understanding that by calling the restore
function on the PyIOError
object, an error would be raised, however, I must be misunderstanding because the compiler seems to consider it a possibility that conn
is not initialised:
error[E0381]: borrow of possibly-uninitialized variable: `conn`
18 | match conn.execute(
| ^^^^ use of possibly-uninitialized `conn`
What would be an appropriate approach here?
First of all, your Ok(t) = conn = t
fails, as you haven't defined conn
. So prior to the match
add let conn;
. Alternatively, you can also just assign the result of the match to conn
.
Second, you still need to return an Err
.
#[pyfunction]
fn do_something(_py: Python) -> PyResult<u32> {
let conn = match Connection::open("database.sql") {
Ok(t) => t,
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py);
return Err(PyErr::fetch(py));
}
};
match conn.execute(
"create table if not exists cats (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
)",
NO_PARAMS,
) {
Ok(_t) => (),
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py);
return Err(PyErr::fetch(py));
}
}
Ok(0)
}
It's been some time since I used PyO3. But unless I remember incorrectly, then you can just remove restore()
also just return the Err
and let PyO3 handle the rest.
#[pyfunction]
fn do_something(_py: Python) -> PyResult<u32> {
let conn = match Connection::open("database.sql") {
Ok(t) => t,
Err(e) => {
let error_message = format!("Unable to open database! {}", e.to_string());
return Err(PyIOError::new_err(error_message));
}
};
match conn.execute(
"create table if not exists cats (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
)",
NO_PARAMS,
) {
Ok(_t) => (),
Err(e) => {
let error_message = format!("Unable to open database! {}", e.to_string());
return Err(PyIOError::new_err(error_message));
}
}
Ok(0)
}