如何为函数指针参数指定通用生存期?

时间:2018-06-04 13:21:47

标签: rust lifetime

背景

Rust中的闭包具有匿名类型,不能称为已知的具体类型。但是from Feb 2017,所有非捕获闭包都可以转换为匿名函数,并且与函数指针具有相同的类型。

我想创建一个标准化curried闭包的类型。如果我们有一个函数fn(T1, T2, ...) -> R,我们可以使用FnXXX(T2, ...) -> R的特殊类型(将XXX替换为MutOnce,或者Box)。然后可以在容器内使用它们而不涉及动态调度。

尝试FnOnce

以下作品:

#![feature(unboxed_closures)]
#![feature(fn_traits)]

struct Curry0<T, R> {
    f: fn(T) -> R,
    v: T,
}
impl<T, R> FnOnce<()> for Curry0<T, R> {
    type Output = R;
    extern "rust-call" fn call_once(self, _: ()) -> R {
        (self.f)(self.v)
    }
}

fn curry<T, R>(f: fn(T) -> R, v: T) -> impl FnOnce() -> R {
    Curry0 { f: f, v: v }
}

fn main() {
    curry(|s| println!("{}", s), "Hello, World!")()
}

但是,我无法添加以下内容:

impl<'a, T, R> FnMut<()> for Curry0<&'a mut T, R> {
    extern "rust-call" fn call_mut(&mut self, _: ()) -> R {
        (self.f)(self.v)
    }
}

错误消息:

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  --> src/main.rs:16:18
   |
16 |         (self.f)(self.v)
   |                  ^^^^^^
   |
note: ...the reference is valid for the lifetime 'a as defined on the impl at 14:1...
  --> src/main.rs:14:1
   |
14 | impl<'a, T, R> FnMut<()> for Curry0<&'a mut T, R> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 15:5
  --> src/main.rs:15:5
   |
15 | /     extern "rust-call" fn call_mut(&mut self, _: ()) -> R {
16 | |         (self.f)(self.v)
17 | |     }
   | |_____^

我的理解是,为了实现这一目标,call_mut必须注意&mut self仅适用于函数本身,因此如果f返回引用v的内容在里面,它将是无效的。

尝试FnMut

要使用FnMut而不是FnOnce,我写道:

#![feature(unboxed_closures)]
#![feature(fn_traits)]

struct Curry0Mut<'a, 'b, T, R>
where
    T: 'a,
    R: 'b,
{
    f: fn(&mut T) -> R,
    v: &'a mut T,
    _l: std::marker::PhantomData<&'b ()>,
}
impl<'a, 'b, T, R> FnOnce<()> for Curry0Mut<'a, 'b, T, R> {
    type Output = R;
    extern "rust-call" fn call_once(self, _: ()) -> R {
        (self.f)(self.v)
    }
}
impl<'a, 'b, T, R> FnMut<()> for Curry0Mut<'a, 'b, T, R>
where
    T: 'a,
    R: 'b,
{
    extern "rust-call" fn call_mut(&mut self, _: ()) -> R {
        (self.f)(self.v)
    }
}

fn curry<'a, T, R>(f: fn(&mut T) -> R, v: &'a mut T) -> impl FnMut() -> R + 'a {
    Curry0Mut {
        f: f,
        v: v,
        _l: std::marker::PhantomData,
    }
}

fn main() {
    let mut v = "Hello, World".to_owned();
    curry(|s| println!("{}", s), &mut v)();
}

这更复杂,遗憾的是我们有两种结构,只是略有不同的用法。当我仔细观察我必须在这里进行的更改时,我发现f实际上是其参数生命周期的通用函数,而T的生命周期与此无关。我们还必须确保R的寿命不比闭包本身长,所以它的生命周期必须在闭包内编码。

我不会详细介绍Fn,因为它类似。只是要注意这会使情况变得更糟,因为我们需要Curry0的另一种变体,因为&mut T不是&T

问题

是否有可能表达v的生命期望值与f s参数不同的事实?

例如,如何编写如下内容:

struct Curry0<'a, 'b, T, R>
where
    R: 'b,
{
    f: fn(T) -> R, //T have generic lifetime
    v: T,          //T: 'a
    _a: std::marker::PhantomData<&'a ()>,
    _b: std::marker::PhantomData<&'b ()>,
}

0 个答案:

没有答案