如何使用通用参数包装函数,这些参数强制使用“静态生命周期”在 Rust 中命名生命周期
How to wrap functions with generic arguments which force a named lifetime in rust with a 'static lifetime
我正在从事一个更大的私人项目,并引入了一些类似函数注册表的东西来使用挂钩。到目前为止效果很好,直到我被迫使用具有命名生命周期的类型在我的例子中是 rusqlite::Transaction<'c>
。因此,所有依赖结构也必须引入命名生命周期,对吗?
最后我遇到了一些编译器生命周期错误,我不知道如何解决它们。
为了缩小问题范围我写了这个示例代码。请注意,您不能更改事务结构,因此这只是任何需要命名生命周期参数的结构的示例。
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(())
}
如果我使用 doSomething
那么它可以工作,但是如果我将 doSomething
放在我的通用支架中,我会遇到这样的生命周期错误:
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
最后,我可以理解问题出现了,因此交易的生命周期与持有人绑定。但是如何告诉 Rust,这个生命周期是一个函数参数并且不必绑定到 Holder-Struct,以便该示例可以安全运行?
如果可行,一个简单的选择是 Holder<T>
但 f: fn(&T)
(以及 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);
}
}
这样 i
的生命周期与 T
的生命周期(如果有的话)是分开的。 h
然后变成 Holder<Transaction>
。当然,前提是你的钩子总是可以在引用上工作,这就足够了。
我正在从事一个更大的私人项目,并引入了一些类似函数注册表的东西来使用挂钩。到目前为止效果很好,直到我被迫使用具有命名生命周期的类型在我的例子中是 rusqlite::Transaction<'c>
。因此,所有依赖结构也必须引入命名生命周期,对吗?
最后我遇到了一些编译器生命周期错误,我不知道如何解决它们。
为了缩小问题范围我写了这个示例代码。请注意,您不能更改事务结构,因此这只是任何需要命名生命周期参数的结构的示例。
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(())
}
如果我使用 doSomething
那么它可以工作,但是如果我将 doSomething
放在我的通用支架中,我会遇到这样的生命周期错误:
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
最后,我可以理解问题出现了,因此交易的生命周期与持有人绑定。但是如何告诉 Rust,这个生命周期是一个函数参数并且不必绑定到 Holder-Struct,以便该示例可以安全运行?
如果可行,一个简单的选择是 Holder<T>
但 f: fn(&T)
(以及 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);
}
}
这样 i
的生命周期与 T
的生命周期(如果有的话)是分开的。 h
然后变成 Holder<Transaction>
。当然,前提是你的钩子总是可以在引用上工作,这就足够了。