Ownership and mutable borrow in same struct

Hi everyone.

I'm new to Rust and so far enjoying the experience very much. As a lot of people, I have some trouble wrapping my head around ownership rules & borrowing. Perhaps someone could help out with an issue I've been stuck at for some time.

I'm trying to wrap/store a connection and row iterator from the postgres crate in my own struct. This new struct will have methods, and I was expecting it to get rid of the connection when done. The issue seems to be that query_raw in Postgres::client obtains a mutable reference to self.

My best attempt so far:

struct MyPgData<'a> {
    client: Client,
    iter: Option<RowIter<'a>>,
}
fn query_postgres<'a>(cfg: &postgres::Config, query: &str) -> Box<MyPgData<'a>> {
    let mut res = Box::new(MyPgData{
        client: cfg.connect(NoTls).unwrap(),
        iter: None
    });
    let iter = res.client.query_raw(query, iter::empty()).unwrap();
    res.iter = Some(iter);
    res
}

Getting the following error: error[E0505]: cannot move out of res because it is borrowed

Very thankful for any help!

For reference, these are the relevant definitions from the postgres crate:

pub fn connect<T>(&self, tls: T) -> Result<Client, Error>
...
pub fn query_raw<'a, T, I>(&mut self, query: &T, params: I) -> Result<RowIter<'_>, Error>
where
    T: ?Sized + ToStatement,
    I: IntoIterator<Item = &'a dyn ToSql>,
    I::IntoIter: ExactSizeIterator,
{
...

It’s extremely helpful if you report the entire compiler output, and not just the error code. In this case, it probably also printed the line where the move occurs, the line where the borrow occurs, and some other diagnostic information.

Of course! Here is the full output:

error[E0505]: cannot move out of `res` because it is borrowed
  --> src/db/pg.rs:50:5
   |
43 | fn query_postgres<'a>(cfg: &postgres::Config, query: &str) -> Box<MyPgData<'a>> {
   |                   -- lifetime `'a` defined here
...
48 |     let iter = res.client.query_raw(query, iter::empty()).unwrap();
   |                ---------- borrow of `res.client` occurs here
49 |     res.iter = Some(iter);
50 |     res
   |     ^^^
   |     |
   |     move out of `res` occurs here
   |     returning this value requires that `res.client` is borrowed for `'a`

error[E0515]: cannot return value referencing local data `res.client`
  --> src/db/pg.rs:50:5
   |
48 |     let iter = res.client.query_raw(query, iter::empty()).unwrap();
   |                ---------- `res.client` is borrowed here
49 |     res.iter = Some(iter);
50 |     res
   |     ^^^ returns a value referencing data owned by the current function

error: aborting due to 2 previous errors

Many thanks!

Hello @jstrat, and welcome :wave:

First of all, know that the return value of query_iter() borrows from the Client. Indeed, let me rewrite it renaming the parameters a bit.

impl Client {
    fn query_raw<'iter, Query : ?Sized, Iterable> (
        self: &'_ mut Client,
        query: &'_ Query,
        params: Iterable,
    ) -> Result<RowIter<'_>, Error>
    where
        Query : ToStatement,
        Iterable : IntoIterator<&'iter (dyn ToSql)>,
        Iterable::IntoIter : ExactSizeIterator,
  • I have also made the lifetime elisions explicit, so that we can see the "holes" where lifetime parameters live.

  • (Aside: for someone starting Rust, you have started tackling quite advanced signatures!)

Let's simplify a bit the bounds (they don't play a role in the signature itself):

impl Client {
    fn query_raw_simplified<Q, I> (
        self: &'_ mut Client,
        query: &'_ Q,
        params: I,
    ) -> RowIter<'_>

so, as we can see, we have a a "hole" lifetime parameter in return position, and two holes in input. The lifetime elision rules dictate that:

  1. For each "hole" in input position, a distinct lifetime parameter is introduced. So here, one for self <-> client and one for query:

    impl Client {
        fn query_raw_simplified<'client, 'query, Q, I> (
            self: &'client mut Client,
            query: &'query Q,
            params: I,
        ) -> RowIter<'_>
    
  2. If there is only one "hole" in input or if there is a borrowed self among the inputs, then that lifetime parameter (only one, or that of the self borrow) is the one used for the lifetime parameters / "holes" in output / return position.
    In this case, the elided lifetime parameter in output position is thus that of 'client:

    impl Client {
        fn query_raw_simplified<'client, 'quiery, Q, I> (
            self: &'client mut Client,
            query: &'query Q,
            params: I,
        ) -> RowIter<'client>
    

And this expresses that the lifetime parameter appearing in the return type is the one that was used for the self input, meaning that the return value is (allowed to be) borrowing from the self Client.

So, now that we know that, let's see your struct definition:

struct MyPgData<'client> {
    client: Client, // <--------------------------+
    iter: Option<RowIter<'client>>, // borrows: --+
}

This is thus what we call a self-referential struct, and bad news, these do not play well with Rust borrowing / ownership rules :grimacing: it's one of the few limitations of the language, but you will soon see that we don't need such self-referential structs that much, at the end of the day.

So, let's refactor a bit your code to avoid them:

  • one option (the easy one) is to use APIs that don't borrow (to avoid having "infectious" lifetime parameters everywhere). Here, for instance, there is the query function which instead of returning a lazily iterared Iterator (RowIter), it collects the results into a Vec of Rows: .query().

  • the other option, when the previous one is not available or is too costly, is to see that here you are constructing your object and making it borrow itself before returning. Instead, split your type into two types: the one constructed (before the borrow happens), and then a second object that borrows the first one: by having a hierarchy (no cycle as with the self-referential struct), things become much easier:

/// Before borrowing
struct MyPgData {
    client: Client,
}
/// Borrowing object
struct MyPgDataQuery<'client> {
    iter: Option<RowIter<'client>>,
    // ...
}

impl MyPgData {
    pub
    fn new ()
      -> Self
    {
        Self {
            client: cfg.connect(NoTls).unwrap(),
        }
    }

    pub
    fn query_postgres (self: &'_ mut Self, query: &'_ str)
      -> MyPgDataQuery<'_>
    {
        MyPgDataQuery {
            iter: self.client.query_raw(query, iter::empty()).unwrap(),
            ...
        }
    }
}
  • (in this example you can even remove the MyPgDataQuery wrapper middle-man, and directly return a RowIter<'_>, it depends on how much functionality / abstraction you want to build onto the return type).

which you can then use as follows:

let mut my_pg_data = MyPgData::new();
let query = my_pg_data.query_postgres();

It may be surprising that splitting this construct magically solves the issue, but know that in Rust, having expressions be bound to actual variable(name)s does carry meaningful semantics (ownership of the expression is given to the variable (name), instead of to a temporary that may be discarded when a statement ends).

6 Likes

Let me tie those compiler messages to @Yandros‘s explanation:

error[E0505]: cannot move out of `res` because it is borrowed
  --> src/db/pg.rs:50:5
   |
43 | fn query_postgres<'a>(cfg: &postgres::Config, query: &str) -> Box<MyPgData<'a>> {
   |                   -- lifetime `'a` defined here
...
48 |     let iter = res.client.query_raw(query, iter::empty()).unwrap();
   |                ---------- borrow of `res.client` occurs here
49 |     res.iter = Some(iter);
50 |     res
   |     ^^^
   |     |
   |     move out of `res` occurs here
   |     returning this value requires that `res.client` is borrowed for `'a`

This says that you’re trying to return the value in res while part of it (res.client) is still borrowed— Rust’s rules require all borrows to be returned before an object is moved (in this case, to the calling function).


error[E0515]: cannot return value referencing local data `res.client`
  --> src/db/pg.rs:50:5
   |
48 |     let iter = res.client.query_raw(query, iter::empty()).unwrap();
   |                ---------- `res.client` is borrowed here
49 |     res.iter = Some(iter);
50 |     res
   |     ^^^ returns a value referencing data owned by the current function

This says that you’re trying to return something that has a reference to one of your local variables. As soon as the function returns, the local variable no longer exists to be referenced.

2 Likes

Wow, what a very thorough answer - thanks a lot!

I can probably refactor my code to split the ownership of the postgres client & the row iterator as you suggest. Going down the path of using the simpler vector-based results functions is not an option since the result set size is unknown, and may be very large.

Self-referential structs are very common in all other programming languages I've used professionally (C++, Java, Kotlin etc). I can't help to think the api I'm experimenting with would be nicer with these two items encapsulated.

Perhaps something like this could be used https://crates.io/crates/owning_ref?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.