Borrowed value does not live long enough - lifetimes and traits

I've been working with Rust for awhile, but I still have more to learn.

I've realized that I have duplicate code and I want to see if I can refactor it using generics and traits. This code is used to generate a sqlx::query based on a REST API request. The struct T implements IntoSql which has a method, SqlArray, that creates an array [(U, bool); N]. U is an enum representing the Columns in the MySql table and implements Into<&str>.

The sql_array method just looks at the values in the struct and calls .is_some() on them and if they are some, they are added to the query. So if the request contains:

{
  "foo": "hello",
  "bar": "world"
}

The sql would look like:

UPDATE table
SET foo = "hello", bar = "world"
WHERE id = ?;

When U is a generic then I get the error: fields_arr does not live long enough (see "fn update_new" in the playground), but when I specify U's type (e.g. User) then I don't get that error (see "fn update"). Any ideas how I can fix this lifetime issue?

Edit: Removed Rust code from the post as it was moved to the playground

Please provide a complete example in the Playground that reproduces your problem. This is too much code ans complexity for us to be able to guess what the root cause is.

I would assume the str representation can only be compile time constants, if so, the easiest fix probably is to explicit use &'static str.

these two line combined is the main cause of the lifetime error. I don't know what your exact use cases look like, but for this example snippet, I think the following bound should be sufficient to satisfy the borrow checker:

pub async fn update<T, U, const N: usize>(
    id: u32,
    t: T,
    table_name: &str, 
    pool: &MySqlPool,
) -> Result<(), Error>
where
    T: IntoSql<U, N, SqlArray = [(U, bool); N]>,
    for <'a> &'a U: Into<&'a str>,
{
    //...
}

but I suggest you define your custom trait instead of using Into<&str> as bound, something like:

trait TableColumn {
    fn name(&self) -> &str;
}
pub async fn update<T, U, const N: usize>(
    id: u32,
    t: T,
    table_name: &str, 
    pool: &MySqlPool,
) -> Result<(), Error>
where
    T: IntoSql<U, N, SqlArray = [(U, bool); N]>,
    U: TableColumn,
{
    //...
}
1 Like