Trying to understand error[E0515]: cannot return value referencing temporary value

I have the following code in a program using duckdb-rs that compiles without error.

/// The context of execution
#[derive(Debug)]
struct Context<'a> {
    /// The connection to the database
    conn: Option<Connection>,

    /// The command line parameters
    cli: &'a Cli,

    /// Identifier of the root directory
    root_id: u64,

    /// Appenders to add rows to tables
    //# apnd_global_error: Option<Appender<'a>>,
}

/// Prepare the context of execution based on command line parameters
fn get_context(cli: &Cli) -> Result<Context, CfaError> {
    let c = &db_open(cli)?;
    let conn = c.try_clone()?;
    //# let apnd_global_error = c.appender("global_error")?;
    
    let ctx = Context {
        conn: Some(conn),
        cli,
        root_id: 0,
        //# apnd_global_error: Some(apnd_global_error),
    };

    Ok(ctx)
}

/// Open the database and returns a connection handle.
fn db_open(cli: &Cli) -> Result<Connection, CfaError> {
...
}

But as soon I uncomment the //# lines, to add an Appender to the context of execution, the compiler raises the error [E0515].

error[E0515]: cannot return value referencing temporary value
   --> src/lib.rs:504:5
    |
480 |     let c = &db_open(cli)?;
    |              ------------- temporary value created here
...
504 |     Ok(ctx)
    |     ^^^^^^^ returns a value referencing data owned by the current function

I understand I can't return a temporary value allocated on the function stack, but I don't understand how ctx would get that temporary. The error from the compiler says that the value is created and assigned to c, but I don't understand how ctx is referencing it. I've tried boxing c to have it created on the heap instead of stack without success.

Can someone explain me what's happening there and orient me how it can be solved?

I would guess the appender() method have type signature like this:

fn appender<'a>(&'a self, name: &str) -> &'a Appender;

or like this:

fn appender<'a>(&'a self, name: &str) -> Appender<'a>;

this means the variable apnd_global_error is borrowing c, and your Context has apnd_global_error as a field, it also borrows c.

Given that the Appender is created from Connection and has some lifetime parameters, this looks like a self-referential struct in disguise. You usually don't want it - you want to store Connection somewhere and only after that create Appenders, not make both in the same function.

1 Like

The appender function signature is pub fn appender(&self, table: &str) -> Result<Appender<'_>>. I did not understood what was the role of the <'_> in the signature. It starts to make sense.

OK. But can I keep them both in the same Context struct later?

It is indeed self-referencial. There's no safe way to get them into the same struct Context<'_> that doesn't cause borrow problems when you try to continue to use the Context<'_>.

You can't move borrowed things, so you can't move the Context while borrowing the Connection within. In this particular case, it would cause a &Connection in the Appender<'_> to dangle.[1] So there's no way to construct the self-referential Context<'_> and then return it.


  1. But even when there's no dangling, it's not allowed. ↩︎

I can imagine the self-references between Connection and Appender<'_>. I would have to read duckdb-rs code to better visualize them.

Thanks all for your help. I'm going to refactor my code to break this self-referencial Context structure.