Reputation: 43
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
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