Dragis
Dragis

Reputation: 43

How to wrap functions with generic arguments which force a named lifetime in rust with a 'static lifetime

I am working on a larger private project and introduced something like a function registry for hooks to be used. That worked well so far, till I was forced to use a a type with a named lifetime In my case it was rusqlite::Transaction<'c>. Due to this all dependend strucs has to introduce a named lifetime too, right?

Finally I got some compiler lifetime errors, I dont know, how to resolve them.

To narrow down the problem I wrote this example code. Please note that you may not change the Transaction-Struct hence this is just a example for any struc require a named lifetime parameter.

use std::error::Error;

struct Holder<T> {
    item: fn(T) -> (),
}

impl <T> Holder<T> {
    fn new(f: fn(T) -> ()) -> Holder<T>{
        Holder{item: f}
    }

    fn exe(&self,i: T){
        let f = self.item;
        f(i);
    }
}

struct Transaction<'c> {
    connection: &'c str,
}

impl <'c> Transaction<'c> {
    fn new(c: &'c str) -> Transaction {
        Transaction{connection: c}
    }
}

fn doSomething(t: &Transaction) {
    println!("I have done Something with {}",&t.connection);
}

pub fn main() -> Result<(), Box<dyn Error>> {

    let h: Holder<&Transaction> = Holder::new(doSomething) ;
    {
        let connection = "c1";
        let tran = Transaction::new(&connection);
        h.exe(&tran);
        h.exe(&tran);
        doSomething(&tran);
        doSomething(&tran);
    }
    {
        let connection = "c2";
        let tran = Transaction::new(&connection);
        h.exe(&tran);
        h.exe(&tran);
        doSomething(&tran);
        doSomething(&tran);
    }

    Ok(())
}

If I use doSomething then it works but if I put doSomething within my generic holder I got lifetime errors like this:

error[E0597]: `tran` does not live long enough
  --> src\t.rs:39:15
   |
   |               ^^^^^ borrowed value does not live long enough
40 |         h.exe(&tran);
41 |     }
   |     - `tran` dropped here while still borrowed
...
45 |         h.exe(&tran);
   |         - borrow later used here

In the end, I could understand that the problem raise, hence the lifetime of the Transaction was bound to the Holder. But how to tell Rust, that this lifetime is a function argument and do not has to be bound to the Holder-Struct, so that the example is safe to work?

Upvotes: 0

Views: 220

Answers (1)

Masklinn
Masklinn

Reputation: 42492

One easy option if that's feasible is to have Holder<T> but f: fn(&T) (and exe: fn(&self, &T):

struct Holder<T> {
    item: fn(&T) -> (),
}

impl <T> Holder<T> {
    fn new(f: fn(&T)) -> Holder<T>{
        Holder{item: f}
    }

    fn exe(&self,i: &T){
        let f = self.item;
        f(i);
    }
}

This way the lifetime of i is separate from the lifetime (if any) of T. h then becomes a Holder<Transaction>. Of course that assumes your hooks can always work on references and that's sufficient.

Upvotes: 1

Related Questions