If you want to bind the lifetime of the input parameter to the lifetime of the return value, you need to define the lifetime parameter for your function and specify it in the types of your input parameter and return value. You can specify any name you want for this lifetime parameter; often when there are several parameters, we simply call them 'a , 'b , 'c , etc.
The Db type accepts the lifetime parameter, but it should not: a Db does not refer to an existing object, so it has no restrictions on the lifespan.
To properly get Db to survive Query , we must write 'a by the borrowed pointer, and not by the lifetime parameter on the Db that we just deleted.
pub fn create_query<'a>(db: &'a Db, query_string: &str) -> Query<'a>
However, this is not enough. If your newtypes do not reference your 'a parameter at all, you will find that Query can really survive Db :
struct Db(*mut ()); struct Query<'a>(*mut ()); // ' fn create_query<'a>(db: &'a Db, query_string: &str) -> Query<'a> { // ' Query(0 as *mut ()) } fn main() { let query; { let db = Db(0 as *mut ()); let q = create_query(&db, ""); query = q;
This is because, by default, the life cycle parameters are bivariant , that is, the compiler can replace the parameter with a longer or shorter lifetime to satisfy the requirements of the caller.
When you store a borrowed pointer in a structure, the lifetime parameter is considered contravariant : this means that the compiler can replace this parameter with a shorter lifetime, but not with a longer lifetime.
We can ask the compiler to treat your life span parameter as contravariant manually by adding the ContravariantLifetime marker to our structure:
use std::marker::ContravariantLifetime; struct Db(*mut ()); struct Query<'a>(*mut (), ContravariantLifetime<'a>); fn create_query<'a>(db: &'a Db, query_string: &str) -> Query<'a> { // ' Query(0 as *mut (), ContravariantLifetime) } fn main() { let query; { let db = Db(0 as *mut ()); let q = create_query(&db, "");
Now the compiler correctly rejects the Query assignment that Db survives.
Bonus: if we change create_query as a Db method, and not as a free function, we can use the compiler lifetime rules for writing out and not write 'a to create_query at all:
use std::marker::ContravariantLifetime; struct Db(*mut ()); struct Query<'a>(*mut (), ContravariantLifetime<'a>); impl Db {
When a method has a self parameter, the compiler will prefer to associate the lifetime of this parameter with the result, even if there are other parameters with the lifetime. However, for free functions, the conclusion is possible only if only one parameter has a lifespan. Here, due to the query_string parameter, which is of type &'a str , there are 2 parameters with a lifetime, so the compiler cannot determine which parameter we want to associate the result with.