0
votes

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?

1
As Masklinn correctly stated was my example solved by simply introducing Refrences within the function signature. It took me some time to discover, that my problem raises again as soon as I use a real Object like String as connection instead of simply a &str.Dragis
Please don't drastically change your question when you hit a new issue. It's much better to accept what helped you and create a completely new question with the new issue. (What you've done now makes Masklinns answer seem like a non-answer as it doesn't fix the current issue and the code is identical to that in your question --- I wasted a while trying to find some subtle difference between them)Michael Anderson
Thanks Michael. You are absolutely right. I changed back this question and asked another one.Dragis

1 Answers

1
votes

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.